Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • msemper/guppy
1 result
Show changes
Commits on Source (79)
== Style guidelines ==
* Don't mix tabs and spaces in one file. It is current# Copyright (C)
2010-2016 Svetlana A. Tkachenko <svetlana@members.fsf.org>
#
# This file is part of guppy.
#
# guppy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# guppy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with guppy. If not, see <http://www.gnu.org/licenses/>.ly recommended to use 4 spaces for indentation (discussion is welcome as long as we agree to convert existing files to a new style).
== TODO ==
Deadline: December 10, 2012
[ ] Core fixes
[ ] set guppy to reconnect after ping timeouts (currently this works unreliably)
[ ] when an exception happens during a xxx plugin load, don't say 'no plugin xxx found' for a next load attempt
[ ] handle ssl warnings
[ ] fix that guppy freezes after a while of idling in a channel
[ ] ircv3 support
[ ] optional core fixes
[ ] split sent lines if too long
[ ] warn when loading a module if it has no docstring
[ ] plugins
[ ] create chansever plugin to update autojoin when guppy joins or parts channels
[ ] add plugin tags functionality ( ie networking plugins, wikimedia plugins, ...; to group plugins other than alphabetical list )
......@@ -6,3 +6,4 @@ conf
*.cfg
*.db
venv
.idea
\ No newline at end of file
1
2
3
# Copyright (C) 2010-2016 Svetlana A. Tkachenko <svetlana@members.fsf.org>
#
# This file is part of guppy.
#
# guppy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# guppy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with guppy. If not, see <http://www.gnu.org/licenses/>.
1) Download
Use your operating system installer to download Guppy on these systems:
* Archlinux
* Windows (coming soon)
Alternatively, use wget to download guppy over http, or git.
* (git instructions coming soon)
2) Directory structure
After downloading guppy, the directory structure will be similar to something like this:
conf/
......@@ -10,11 +40,11 @@ After downloading guppy, the directory structure will be similar to something li
The extra/ directory contains plugins that are either unfinished or very specific, you will usually not need them. (You can put them in plugins/ before running the install script, if you need them, but do please read them first if you decide to do so.)
The dist/ directory contains installation scripts for certain operating systems, this may be useful if you are using them. Currently available install scripts are for:
The dist/ directory contains installation scripts for supported operating systems.
Archlinux
3) Configuration
If this is not your distro, run the guppy executably once to launch the installer script.
Run the guppy executably once to launch the installer script.
$ ./guppy
......@@ -22,12 +52,13 @@ It will prompt you with a list of plugins. Choose carefully, the default is mini
$ ./guppy
__ _ _ _ _ __ _ __ _ _
__ _ _ _ _ __ _ __ _ _
/ _` | | | | '_ \| '_ \| | | | http://repo.or.cz/w/guppy.git/summary
| (_| | |_| | |_) | |_) | |_| | irc://irc.freenode.net/guppy
\__, |\__,_| .__/| .__/ \__, | http://guppy.gshellz.org
|___/ |_| |_| |___/ version 0.4.3
\__, |\__,_| .__/| .__/ \__, | http://guppy.uk.to
|___/ |_| |_| |___/ version 0.4.4
Settings directory: /home/test/tests/ircd/guppy/conf/
No configuration file found, running makeconf
What IRC network do I connect to (just the name)? []: gnu
......@@ -36,6 +67,8 @@ It will prompt you with a list of plugins. Choose carefully, the default is mini
What nickname should I use? []:
...
4) Running in background
After the configuration, the binary will exit, and you will need to start it again in screen or tmux with no arguments; guppy does not fork into the background.
$ screen -U ./guppy
......@@ -45,6 +78,8 @@ or
$ tmux ./guppy
5) Authentication
Once the bot joins the channel, if you loaded auth plugin, you will need to set up your password. Otherwise anyone who uses your nickname can control the bot. Use the "passwd" command in PM with the bot to set a new password.
<rob> passwd lam3r lam3r
......
Source: http://repo.or.cz/w/guppy.git
License: GPLv3.
# Copyright (C) 2010-2016 Svetlana A. Tkachenko <svetlana@members.fsf.org>
#
# This file is part of guppy.
#
# guppy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# guppy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with guppy. If not, see <http://www.gnu.org/licenses/>.
Website: http://guppy.uk.to
Channel: irc://irc.freenode.net/guppy
Feedback means are #guppy at freenode, and the feedback form on the website.
Feedback means are #guppy at freenode.
For more information about the freenode IRC network, supporting free
and open-source communities since 1998, please visit www.freenode.net.
For installation instructions, please see the INSTALL file.
# Copyright (C) 2010-2016 Svetlana A. Tkachenko <svetlana@members.fsf.org>
#
# This file is part of guppy.
#
# guppy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# guppy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with guppy. If not, see <http://www.gnu.org/licenses/>.
== Style guidelines ==
* Don't mix tabs and spaces in one file. It is currently recommended to use 4 spaces for indentation (discussion is welcome as long as we agree to convert existing files to a new style).
......
# Copyright (C) 2010-2016 Svetlana A. Tkachenko
#
# This file is part of guppy.
#
# guppy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# guppy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with guppy. If not, see <http://www.gnu.org/licenses/>.
For debugging, to make a unix OS user lose internet connection, run
# iptables -A OUTPUT -o eth0 -m owner --uid-owner 1002 -j DROP
......
#
# Copyright (C) 2010-2016 Svetlana A. Tkachenko
#
# This file is part of guppy.
#
# guppy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# guppy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with guppy. If not, see <http://www.gnu.org/licenses/>.
This page is a git cheatsheet. git is recommended for contributing to guppy, but is not required.
(As always, manpages are the grand superior resource.)
......
#
# Copyright (C) 2010-2016 Svetlana A. Tkachenko
#
# This file is part of guppy.
#
# guppy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# guppy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with guppy. If not, see <http://www.gnu.org/licenses/>.
what guppy does when it has a ns username and password set is:
'/msg nickserv identify accountname pwd' onconnect
some networks want '/msg nickserv identify pwd'
......
# Copyright (C) 2010-2016 Svetlana A. Tkachenko
#
# This file is part of guppy.
#
# guppy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# guppy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with guppy. If not, see <http://www.gnu.org/licenses/>.
Wishlist
Attention - this wishlist is outdated and the homepage is being updated to include a bug tracker!
== Core ==
* Implement [[http://ircv3.atheme.org/|IRC "v3"]] compatibility.
* Implement [[http://ircv3.atheme.org/|IRC "v3"]] compatibility (where reasonable).
* <del>Implement ''self.server.mynick'' to reflect nick changes, after which bot nick is not the conf nick.</del> Unneeded?
* Adding tags/categories for available commands could increase usability.
* Read [[http://tools.ietf.org/html/rfc1459|RFC1459]] and add any issues to 'current'.
......
"""
We need the decorator, otherwise the
bot doesn't see it's a plugin
"""
# Copyright (C) 2010-2016 Svetlana A. Tkachenko <svetlana@members.fsf.org>
# Copyright (C) 2011 David Vo <david.vo2@gmail.com>
#
# This file is part of guppy.
#
# guppy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# guppy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with guppy. If not, see <http://www.gnu.org/licenses/>.
# We need the decorator, otherwise the
# bot doesn't see it's a plugin
@plugin
class Printer(object):
# The class name is the plugin name, case insensitive
......@@ -25,7 +41,7 @@ class Printer(object):
It doesn't need to be defined, it is passed as an argument to
self.server.handle("command", function, ["command1", "command2"])
"""
self.commands = [ "ping" ]
self.commands = ["ping"]
"""
this is just so that we don't have to hard-code every event
......@@ -33,7 +49,7 @@ class Printer(object):
but you can do it like "self.server.handle("event", self.function)"
"""
for h in self.server.handlers:
func = getattr(self, "handle_"+h, None)
func = getattr(self, "handle_" + h, None)
if func is not None:
if h == "command":
self.server.handle(h, func, self.commands)
......@@ -61,15 +77,15 @@ class Printer(object):
#in case we are handling a lot of commands
if cmd == "ping":
#send a response
self.server.doMessage(channel, user[0]+": Pong")
self.server.doMessage(channel, user[0] + ": Pong")
def handle_connect(self, server):
""" Called when we connect to the server """
self.prnt("I have connected to %s" % server)
self.prnt("I have connected to " + server)
def handle_disconnect(self, server):
""" Called when we disconnect from the server """
self.prnt("I have disconnected from %s" % server)
self.prnt("I have disconnected from " + server)
def handle_message(self, channel, user, message):
""" Called when a message is received """
......@@ -117,4 +133,3 @@ class Printer(object):
of the user who had the mode set upon them
"""
self.prnt("%s set mode %s on %s" % (user[0], mode, otheruser if otheruser != "" else channel))
......@@ -18,7 +18,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
import random
#import random
UNODECK = ['b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'g0', 'g1', 'g2', 'g3', 'g4', 'g5', 'g6', 'g7', 'g8', 'g9', 'g1', 'g2', 'g3', 'g4', 'g5', 'g6', 'g7', 'g8', 'g9', 'y0', 'y1', 'y2', 'y3', 'y4', 'y5', 'y6', 'y7', 'y8', 'y9', 'y1', 'y2', 'y3', 'y4', 'y5', 'y6', 'y7', 'y8', 'y9', 'r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'rS', 'gS', 'bS', 'yS', 'rD2', 'gD2', 'bD2', 'yD2', 'rR', 'gR', 'bR', 'yR', 'wW', 'wW', 'wW', 'wW', 'wWD4', 'wWD4', 'wWD4', 'wWD4', 'rS', 'gS', 'bS', 'yS', 'rD2', 'gD2', 'bD2', 'yD2', 'rR', 'gR', 'bR', 'yR']
......@@ -33,4 +33,3 @@ class Uno(object):
def handle_command(self, channel, user, cmd, args):
pass
......@@ -33,7 +33,8 @@ class Ping_server(object):
self.loop = True
self.ponged = False
self.timeLoop = 10
def loop(plugin,server):
def loop(plugin, server):
timeWaited = 0
while self.loop:
if timeWaited >= self.timeLoop:
......@@ -41,10 +42,11 @@ class Ping_server(object):
timeWaited = 0
time.sleep(5)
timeWaited += 5
self.t1 = threading.Thread(target=loop, args=(self,server,))
self.t1 = threading.Thread(target=loop, args=(self, server,))
self.t1.daemon = True
self.t1.start()
server.handle("data", self.handle_data)
def pingServer(self):
if (self.ponged == False):
self.server.doQuit()
......@@ -52,5 +54,6 @@ class Ping_server(object):
self.server.sendLine('ping :0000')
self.ponged = False
def handle_data(self, network, data):
self.ponged = True
# guppy Copyright (C) 2010-2011 guppy team members.
#
# This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
# This is free software, and you are welcome to redistribute it
# under certain conditions; type `show c' for details.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
import time
import datetime
from datetime import datetime as dt
import os
import sqlite3
import threading
import urllib.request
import xml.etree.ElementTree as ET
@plugin
class Rss(object):
def __init__(self, server):
# Variables.
self.server = server
self.network = self.server.config["network"]
self.commands = ["rss-add", "rss-del", "rss-list"]
self.loop = True
self.server.handle("command", self.handle_command, self.commands)
self.network = self.server.config["network"]
# Define and/or create database.
self.db = os.path.join(self.server.config["confdir"],"rss.db")
self.db_exists = os.path.isfile(self.db)
self.connection = sqlite3.connect(self.db)
self.cursor = self.connection.cursor()
if (not self.db_exists):
# create the db
self.cursor.execute('''CREATE TABLE rss (id text, network text, channel text, url text)''')
self.connection.commit()
print("[DEBUG] [rss] Database created for the first time")
# Set up the thread to check for new feeds.
self.timeCheckFeeds = 60
def loop(plugin,server):
timeWaited = 0
while self.loop:
if timeWaited >= self.timeCheckFeeds:
self.checkFeeds()
timeWaited = 0
time.sleep(10)
timeWaited += 10
self.t1 = threading.Thread(target=loop, args=(self,server,))
self.t1.daemon = True
self.t1.start()
def handle_command(self, channel, user, cmd, args):
connection = sqlite3.connect(self.db)
cursor = connection.cursor()
if (cmd == 'rss-add'):
url = args[0]
cursor.execute('''SELECT url FROM rss WHERE network = ? and channel = ? and url = ?''', (self.network, channel, url))
if len(cursor.fetchall()) == 0:
cursor.execute('''INSERT INTO rss VALUES(?,?,?,?)''', ('', self.network, channel, url))
connection.commit()
self.server.doMessage(channel, "Done. RSS item %s added to %s.", (url, channel))
else:
self.server.doMessage(channel, "Error: RSS item %s already exists at %s" % (url, channel))
elif (cmd == 'rss-list'):
cursor.execute('''SELECT url FROM rss WHERE network = ? and channel = ?''', (self.network, channel))
for my_url in cursor.fetchall():
self.server.doMessage(channel, "RSS item: %s" % my_url[0])
elif (cmd == 'rss-del'):
url = args[0]
cursor.execute('''SELECT url FROM rss WHERE network = ? and channel = ? and url = ?''', (self.network, channel, url))
if len(cursor.fetchall()) == 0:
self.server.doMessage(channel, "Error: %s RSS feed not found" % url)
else:
try:
cursor.execute('''DELETE FROM rss WHERE network = ? and channel = ? and url = ?''', (self.network, channel, url))
self.server.doMessage(channel, "%s RSS feed removed" % url)
connection.commit()
except sqlite3.Error as e:
self.server.doMessage(channel, "Error: %s RSS feed not removed (%s)" % (args[0], e.args[0]))
cursor.close()
connection.close()
def announceFeed(self, row):
print(str(row))
# row = ('1', 'gnu', '#guppy', 'http://phys.org/rss-feed/')
url = row[3]
last_known_id = row[0]
bool_updated = 0
request = urllib.request.Request(url, headers = {'user-agent': 'guppy '+self.server.config["version"]})
s = urllib.request.urlopen(request)
s = s.read()
root = ET.fromstring(s)
for channel in root:
children = channel.findall('item')
for child in children:
feed_id = child.find('guid').text
if (feed_id == last_known_id): return
elif (bool_updated == 0):
# update the db
connection = sqlite3.connect(self.db)
cursor = connection.cursor()
print('UPDATE rss SET id = %s where network = %s and channel = %s and url = %s'%(feed_id, row[1], row[2], row[3]))
cursor.execute("UPDATE rss SET id = ? where network = ? and channel = ? and url = ?", (feed_id, row[1], row[2], row[3]))
connection.commit()
connection.close()
bool_updated = 1
title = child.find('title').text
url = child.find('link').text
url = urllib.request.urlopen("http://tinyurl.com/api-create.php?url="+url).readline().decode('utf8')
msg = url + " "
if (child.find('category') is not None):
msg += "["+child.find('category').text+"]" + " "
msg += title
self.server.doMessage(row[2], msg)
# print("%s [%s] %s" % (str(url), str(cat), str(title)))
def checkFeeds(self):
connection = sqlite3.connect(self.db)
cursor = connection.cursor()
cursor.execute('SELECT * FROM rss')
print("Got the rows...")
for row in cursor.fetchall():
self.announceFeed(row)
cursor.close()
# guppy Copyright (C) 2010-2011 guppy team members.
# Copyright (C) 2010-2016 Svetlana A. Tkachenko <svetlana@members.fsf.org>
#
# This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
# This is free software, and you are welcome to redistribute it
# under certain conditions; type `show c' for details.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This file is part of guppy.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# guppy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
# guppy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with guppy. If not, see <http://www.gnu.org/licenses/>.
import time, threading
import json
......@@ -37,12 +34,13 @@ class wikinews(object):
self.commands = []
self.loop = True
self.channel = "#wikinews-spam"
self.timeListAllFeeds = 24*60*60 # 24h
self.timeCheckNewEntries = 1*60 # 1 minutes
self.timeCheckPublished = 1*60 # 1 minute
self.timeListAllFeeds = 24 * 60 * 60 # 24h
self.timeCheckNewEntries = 1 * 60 # 1 minutes
self.timeCheckPublished = 1 * 60 # 1 minute
self.lastEntries = []
self.url='http://en.wikinews.org/w/api.php?action=query&list=categorymembers&format=json&cmtitle=Category%3AReview&cmprop=ids%7Ctitle%7Ctimestamp&cmnamespace=0&cmlimit=10&cmsort=timestamp'
def loop(plugin,server):
self.url = 'http://en.wikinews.org/w/api.php?action=query&list=categorymembers&format=json&cmtitle=Category%3AReview&cmprop=ids%7Ctitle%7Ctimestamp&cmnamespace=0&cmlimit=10&cmsort=timestamp'
def loop(plugin, server):
timeWaited = 0
while self.loop:
if timeWaited >= self.timeListAllFeeds:
......@@ -50,7 +48,8 @@ class wikinews(object):
timeWaited = 0
time.sleep(10)
timeWaited += 10
def loop2(plugin,server):
def loop2(plugin, server):
timeWaited = 0
while self.loop:
if timeWaited >= self.timeCheckNewEntries:
......@@ -58,7 +57,8 @@ class wikinews(object):
timeWaited = 0
time.sleep(10)
timeWaited += 10
def loop3(plugin,server):
def loop3(plugin, server):
timeWaited = 0
while self.loop:
if timeWaited >= self.timeCheckPublished:
......@@ -66,9 +66,9 @@ class wikinews(object):
timeWaited = 0
time.sleep(10)
timeWaited += 10
self.t1 = threading.Thread(target=loop, args=(self,server,))
self.t2 = threading.Thread(target=loop2, args=(self,server,))
self.t3 = threading.Thread(target=loop3, args=(self,server,))
self.t1 = threading.Thread(target=loop, args=(self, server,))
self.t2 = threading.Thread(target=loop2, args=(self, server,))
self.t3 = threading.Thread(target=loop3, args=(self, server,))
self.t1.daemon = True
self.t2.daemon = True
self.t3.daemon = True
......@@ -76,12 +76,14 @@ class wikinews(object):
self.t2.start()
self.t3.start()
server.handle("message", self.handle_message)
def handle_message(self, channel, nick, message):
matchesList = re.findall("\[\[(.*?)\]\]",message)
if matchesList == []: return
urlsList=[]
matchesList = re.findall("\[\[(.*?)\]\]", message)
if matchesList == []:
return
urlsList = []
for match in matchesList:
url="https://en.wikinews.org/w/api.php?action=query&prop=info&format=json&inprop=url&iwurl=1&titles="+urllib.parse.quote(match)
url = "https://en.wikinews.org/w/api.php?action=query&prop=info&format=json&inprop=url&iwurl=1&titles=" + urllib.parse.quote(match)
try:
data = json.loads(urllib.request.urlopen(url).read().decode("utf8"))['query']
except:
......@@ -97,7 +99,8 @@ class wikinews(object):
return
pageUrl = bytes(pageUrl, "utf-8").decode("unicode_escape")
urlsList.append(pageUrl)
self.server.doMessage(channel, "%s" % " ".join(urlsList))
self.server.doMessage(channel, " ".join(urlsList))
def checkNewEntries(self):
try:
response = urllib.request.urlopen(self.url).read()
......@@ -113,6 +116,7 @@ class wikinews(object):
self.lastEntries.append(entry['pageid'])
if entry['pageid'] not in lastentries:
self.listEntry(entry, "submitted for review")
def listAllFeeds(self):
#
self.lastEntries = []
......@@ -131,21 +135,23 @@ class wikinews(object):
for entry in j['query']['categorymembers']:
self.lastEntries.append(entry['pageid'])
self.listEntry(entry, "submitted for review")
def listEntry(self,entry, comment):
url2 = "https://en.wikinews.org/w/api.php?action=query&prop=info&format=json&inprop=url&pageids="+str(entry['pageid'])
def listEntry(self, entry, comment):
url2 = "https://en.wikinews.org/w/api.php?action=query&prop=info&format=json&inprop=url&pageids=" + str(entry['pageid'])
try:
fullurl = json.loads(urllib.request.urlopen(url2).read().decode("utf8"))['query']['pages'][str(entry['pageid'])]['fullurl']
fullurl = urllib.request.urlopen("http://tinyurl.com/api-create.php?url="+fullurl).readline().decode('utf8')
fullurl = urllib.request.urlopen("http://tinyurl.com/api-create.php?url=" + fullurl).readline().decode('utf8')
except:
self.server.prnt("Exception occured, ignoring -- START")
traceback.print_exc()
self.server.prnt("Exception occured, ignoring -- END")
return
hoursAgo = datetime.datetime.utcnow() - datetime.datetime.strptime(entry['timestamp'], "%Y-%m-%dT%H:%M:%SZ")
hoursAgo = datetime.timedelta(hoursAgo.days,hoursAgo.seconds)
hoursAgo = datetime.timedelta(hoursAgo.days, hoursAgo.seconds)
self.server.doMessage(self.channel, "%s %s *%s* ago - %s" % (fullurl, comment, hoursAgo, entry['title']))
def checkPublished(self):
url='https://en.wikinews.org/w/api.php?action=query&list=categorymembers&format=json&cmtitle=Category%3APublished&cmprop=ids%7Ctitle%7Ctimestamp&cmnamespace=0&cmlimit=10&cmsort=timestamp&cmdir=desc'
url = 'https://en.wikinews.org/w/api.php?action=query&list=categorymembers&format=json&cmtitle=Category%3APublished&cmprop=ids%7Ctitle%7Ctimestamp&cmnamespace=0&cmlimit=10&cmsort=timestamp&cmdir=desc'
try:
response = urllib.request.urlopen(url).read()
except:
......@@ -161,6 +167,7 @@ class wikinews(object):
self.listEntry(entry, "published")
else:
break
def destroy(self):
self.loop = False
self.t1.join()
......
#!/usr/bin/env python3
# guppy Copyright (C) 2010-2011 guppy team members.
#
# This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
# This is free software, and you are welcome to redistribute it
# under certain conditions; type `show c' for details.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# Copyright (C) 2010-2016 Svetlana A. Tkachenko
# Copyright (C) 2010-2016 David Vo
# Copyright (C) 2010-2016 aLasterShark
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# This file is part of guppy.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
# guppy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# guppy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with guppy. If not, see <http://www.gnu.org/licenses/>.
import sys
......@@ -36,16 +36,16 @@ import time
class main(object):
def __init__(self):
# Constant.
self.version = "0.4.3"
self.version = "0.4.4"
self.homedir = os.path.abspath(os.path.dirname(__file__))
print(" __ _ _ _ _ __ _ __ _ _ ")
print(" / _` | | | | '_ \| '_ \| | | | http://repo.or.cz/w/guppy.git/summary")
print("| (_| | |_| | |_) | |_) | |_| | irc://irc.freenode.net/guppy")
print(" \__, |\__,_| .__/| .__/ \__, | ")
print((" |___/ |_| |_| |___/ version "+self.version))
print("PID: "+str(os.getpid()))
print(" \__, |\__,_| .__/| .__/ \__, | http://guppy.uk.to")
print(" |___/ |_| |_| |___/ version " + self.version)
print("PID: " + str(os.getpid()))
self.directory = self.homedir + '/conf/'
print(("Settings directory: %s" % self.directory))
print("Settings directory: " + self.directory)
if not os.path.exists(self.directory):
os.makedirs(self.directory)
......@@ -72,7 +72,7 @@ optional arguments:
if "-c" in sys.argv[1:] or "--makeconf" in sys.argv[1:]:
self.makeconf()
if os.path.isfile(self.configpath) == False:
if not os.path.isfile(self.configpath):
print("No configuration file found, running makeconf")
self.makeconf()
......@@ -82,16 +82,13 @@ optional arguments:
def start(self):
self.clients = []
for section in self.config.sections():
server_config = {}
server_config["network"] = section
server_config["version"] = self.version
server_config["confdir"] = self.directory
for item,value in self.config.items(section):
server_config = {"network": section, "version": self.version, "confdir": self.directory}
for item, value in self.config.items(section):
server_config[item] = value
print("%s [%s] Connecting..." % (time.strftime("%Y-%m-%d %H:%M:%S"),server_config["network"]))
print("%s [%s] Connecting..." % (time.strftime("%Y-%m-%d %H:%M:%S"), server_config["network"]))
self.clients.append(irc.IRC(server_config))
while(1):
while 1:
try:
asyncore.loop()
except:
......@@ -102,19 +99,19 @@ optional arguments:
client.doQuit("Keyboard interrupt.")
client.pluginManager.unloadAllPlugins()
def my_raw(self,prompt="", default=""):
ret = eval("'"+input("%s [%s]: "%(prompt,default))+"'")
def my_raw(self, prompt="", default=""):
ret = eval("'" + input("%s [%s]: " % (prompt, default)) + "'")
return default if ret == "" else ret
def set(self,section,option,text,default=""):
def set(self, section, option, text, default=""):
try:
if default == "":
default = self.config.get(section,option)
default = self.config.get(section, option)
except configparser.NoOptionError:
pass
value = self.my_raw(text,default)
self.config.set(section,option,value)
value = self.my_raw(text, default)
self.config.set(section, option, value)
def makeconf(self):
self.config = configparser.RawConfigParser()
......@@ -126,30 +123,31 @@ optional arguments:
except configparser.DuplicateSectionError:
overwrite = self.my_raw("Server already exists! Overwrite? (yes/no)", "no").lower()
while overwrite != "yes" and overwrite != "y" and overwrite != "no" and overwrite != "n":
overwrite = self.my_raw("Invalid option. Overwrite configuration for "+currentNetwork+"? (yes/no)", "no").lower()
overwrite = self.my_raw("Invalid option. Overwrite configuration for " + currentNetwork + "? (yes/no)", "no").lower()
if overwrite.lower() == "no" or overwrite.lower() == "n":
continue #go back to top of "while getting_servers:"
continue # go back to top of "while getting_servers:"
#else continue out of try/except
self.set(currentNetwork,'host',"What is the IRC network address (eg. irc.example.org)?")
self.set(currentNetwork,'channels',"What channels to automatically join (comma-separated, no spaces)?")
self.set(currentNetwork,'nickname',"What nickname should I use?")
self.set(currentNetwork,'ident',"What username (ident) should I use?")
self.set(currentNetwork,'owner_nick',"What is YOUR nick (used to auth, you can set password later)?")
self.set(currentNetwork,'ns_name',"What is my nickServ username (if there is none, press ENTER)?")
self.set(currentNetwork,'ns_pwd',"What is my NickServ password (if there is none, press ENTER)?")
self.set(currentNetwork,'srpass',"What server password should I use (used on private servers or for Services auth)?")
self.set(currentNetwork,'port',"What port should I use?", "6667")
self.set(currentNetwork,'use_ssl','Should I use ssl (yes/no)?', 'no')
self.set(currentNetwork,'ipv6','Should I use IPv6 to connect (yes/no)?', 'no')
self.set(currentNetwork,"comchar","What command char (other than my nick) should I respond to?", "-")
list1 = os.listdir(self.homedir+"/plugins")
list2 = [item.replace(".py","") for item in list1 if not '__' in item]
print("Available plugins are: "+" ".join(list2))
self.set(currentNetwork,"plugins","What plugins will I load on startup (comma-separated, no spaces)?", "auth,printer,pluginloader,ping_server")
another_server = self.my_raw("All done with "+currentNetwork+". Add another server? (yes/no)", "no").lower()
self.set(currentNetwork, 'host', "What is the IRC network address (eg. irc.example.org)?")
self.set(currentNetwork, 'channels', "What channels to automatically join (comma-separated, no spaces)?")
self.set(currentNetwork, 'nickname', "What nickname should I use?")
self.set(currentNetwork, 'ident', "What username (ident) should I use?")
self.set(currentNetwork, 'owner_nick', "What is YOUR nick (used to auth, you can set password later)?")
self.set(currentNetwork, 'ns_name', "What is my nickServ username (if there is none, press ENTER)?")
self.set(currentNetwork, 'ns_pwd', "What is my NickServ password (if there is none, press ENTER)?")
self.set(currentNetwork, 'srpass', "What server password should I use (used on private servers or for Services auth)?")
self.set(currentNetwork, 'port', "What port should I use?", "6667")
self.set(currentNetwork, 'use_ssl', 'Should I use ssl (yes/no)?', 'no')
self.set(currentNetwork, 'ipv6', 'Should I use IPv6 to connect (yes/no)?', 'no')
self.set(currentNetwork, "comchar", "What command char (other than my nick) should I respond to?", "-")
self.set(currentNetwork, "infochar", "What command char should I use for the info plugin?", "~") ###N# Asks for and writes the infochar to the main.cfg
list1 = os.listdir(self.homedir + "/plugins")
list2 = [item.replace(".py", "") for item in list1 if not '__' in item]
print("Available plugins are: " + " ".join(list2))
self.set(currentNetwork, "plugins", "What plugins will I load on startup (comma-separated, no spaces)?", "auth,printer,pluginloader,ping_server")
another_server = self.my_raw("All done with " + currentNetwork + ". Add another server? (yes/no)", "no").lower()
while another_server != "yes" and another_server != "y" and another_server != "no" and another_server != "n":
another_server = self.my_raw("Invalid option. Do you want to add another server? (yes/no)", "no").lower()
......@@ -179,3 +177,4 @@ if __name__ == "__main__":
print("Shutting down all connections...")
c.stop()
print("Quitting!")
# guppy Copyright (C) 2010-2011 guppy team members.
#
# This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
# This is free software, and you are welcome to redistribute it
# under certain conditions; type `show c' for details.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# Copyright (C) 2010-2016 Svetlana A. Tkachenko
# Copyright (C) 2010-2016 David Vo
# Copyright (C) 2010-2016 FurryHead
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# This file is part of guppy.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
import socket, os, threading, re, asynchat, traceback, ssl
# guppy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# guppy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with guppy. If not, see <http://www.gnu.org/licenses/>.
#import os, threading
import socket, re, asynchat, traceback, ssl
import plugins
import time
import os
class User(str):
......@@ -42,17 +43,17 @@ class PluginManager(object):
self.plugins = {}
self.prnt = server.prnt
self.handlers = {
"join": [ ],
"part": [ ],
"quit": [ ],
"message": [ ],
"connect": [ ],
"disconnect": [ ],
"action": [ ],
"nick": [ ],
"kick": [ ],
"mode": [ ],
"command": { },
"join": [],
"part": [],
"quit": [],
"message": [],
"connect": [],
"disconnect": [],
"action": [],
"nick": [],
"kick": [],
"mode": [],
"command": {},
}
def handle(self, event, handler_func, handler_commands=None):
......@@ -86,12 +87,13 @@ class PluginManager(object):
def event(self, eventName, *args):
eventName = eventName.lower()
if self.handlers.get(eventName, None) is None: return
if self.handlers.get(eventName, None) is None:
return
if eventName == "command":
handler = self.handlers[eventName].get(args[2].lower(), None)
if handler is None:
return #no such command
return # no such command
handler(*args)
else:
......@@ -128,13 +130,13 @@ class PluginManager(object):
def unloadPlugin(self, pluginName):
if self.loadedPlugin(pluginName.lower()):
# the plugin is loaded
inst = self.plugins[pluginName.lower()] # the plugin object
del self.plugins[pluginName.lower()] # delete plugins object from self.plugins
inst = self.plugins[pluginName.lower()] # the plugin object
del self.plugins[pluginName.lower()] # delete plugins object from self.plugins
for event, eList in list(self.handlers.items()):
if event == "command":
newList = {}
destructors = []
for cmd,func in list(eList.items()):
for cmd, func in list(eList.items()):
if func.__self__ == inst:
destructor = getattr(inst, "destroy", None)
if destructor is not None and destructor not in destructors:
......@@ -159,11 +161,13 @@ class PluginManager(object):
self.handlers[event] = newList
for f in destructors:
f()
if getattr(inst, "destroy", None) != None: inst.destroy()
if getattr(inst, "destroy", None) is not None:
inst.destroy()
def loadAllPlugins(self):
plugins = [k for k in self.server.config["plugins"].split(",") if k != '']
for plugin in plugins:
self.server.prnt("Loading "+plugin)
self.server.prnt("Loading " + plugin)
self.loadPlugin(plugin.lower())
def unloadAllPlugins(self):
......@@ -173,8 +177,9 @@ class PluginManager(object):
class IRC(asynchat.async_chat):
def handle_error(self):
'''Print a traceback when an error happens'''
"""Print a traceback when an error happens"""
traceback.print_exc()
def __init__(self, config):
asynchat.async_chat.__init__(self)
self.pluginManager = PluginManager(self)
......@@ -184,15 +189,17 @@ class IRC(asynchat.async_chat):
self.handlers = self.pluginManager.handlers
self.plugins = self.pluginManager.plugins
self.getPlugin = self.pluginManager.getPlugin
errs = self.pluginManager.loadAllPlugins()
self.pluginManager.loadAllPlugins()
self.userlist = { }
self.userlist = {}
self.data = b""
self.set_terminator(b"\r\n")
# check ipv6 flag (added in a recent version, may not exist)
if ('ipv6' in self.config) and self.config["ipv6"].lower().startswith("y"): stype = socket.AF_INET6
else: stype = socket.AF_INET
if ('ipv6' in self.config) and self.config["ipv6"].lower().startswith("y"):
stype = socket.AF_INET6
else:
stype = socket.AF_INET
# create socket
self.tup = socket.getaddrinfo(self.config["host"], int(self.config["port"]), stype)[0][4]
self.create_socket(stype, socket.SOCK_STREAM)
......@@ -204,7 +211,7 @@ class IRC(asynchat.async_chat):
try:
self.connect(self.tup)
except ssl.SSLError as error:
self.prnt('SSL Error connecting to server. (Are you using the right port?) Error message: %s' % error)
self.prnt('SSL Error connecting to server. (Are you using the right port?) Error message: ' + error)
return
except socket.error as error:
self.prnt('There was an error connecting to %s. %s' % (self.config["host"], error))
......@@ -237,13 +244,13 @@ class IRC(asynchat.async_chat):
if words[1] == "433":
#There was a nick collision
self.config["nickname"] = self.config["nickname"] + "_"
self.sendLine("NICK "+self.config["nickname"])
self.config["nickname"] += "_"
self.sendLine("NICK " + self.config["nickname"])
elif words[1] == "422" or words[1] == "376":
# We successfully logged in, do post-login procedures
if self.config["ns_name"] != "" and self.config["ns_pwd"] != "":
self.doMessage("NickServ", "IDENTIFY "+self.config["ns_name"]+" "+self.config["ns_pwd"])
self.doMessage("NickServ", "IDENTIFY " + self.config["ns_name"] + " " + self.config["ns_pwd"])
self.pluginManager.event("connect", self.config["network"])
......@@ -267,7 +274,7 @@ class IRC(asynchat.async_chat):
if words[2] == self.config["nickname"]:
isPrivate = True
message = data[data.find(":", data.find(channel[(channel.find("-") == -1 and 1 or channel.find("-")):]))+1:]
message = data[data.find(":", data.find(channel[(channel.find("-") == -1 and 1 or channel.find("-")):])) + 1:]
if message.find("\x01ACTION") == 0:
# String was found, it's an action
self.pluginManager.event("action", channel, user, message[8:-1])
......@@ -286,7 +293,7 @@ class IRC(asynchat.async_chat):
self.pluginManager.event("message", channel, user, message)
self.pluginManager.event("command", channel, user, cmd.lower(), args)
else:
if re.match("^"+self.config["nickname"]+"[^A-Za-z0-9] .+$", message, flags=re.IGNORECASE):
if re.match("^" + self.config["nickname"] + "[^A-Za-z0-9] .+$", message, flags=re.IGNORECASE):
#Someone addressed us, it's a command (probably)
args = message.split(" ")
cmd = args[1]
......@@ -330,7 +337,7 @@ class IRC(asynchat.async_chat):
# Someone set a mode
try:
self.pluginManager.event("mode", words[2], user, words[3], words[4])
except IndexError: # words[4] is not valid, it wasn't set on a user
except IndexError: # words[4] is not valid, it wasn't set on a user
self.pluginManager.event("mode", words[2], user, words[3], "")
elif words[1] == "KICK":
......@@ -352,38 +359,44 @@ class IRC(asynchat.async_chat):
def handle_connect(self):
self.config["ident"] = ident = self.config["ident"] if self.config["ident"] != "" else self.config["nickname"]
# pass, nick, user - http://tools.ietf.org/html/rfc1459#section-4.1
if self.config["srpass"] != "": self.sendLine("PASS "+self.config["srpass"])
self.sendLine("NICK "+self.config["nickname"])
self.sendLine("USER "+ident+" * * *")
if self.config["srpass"] != "":
self.sendLine("PASS " + self.config["srpass"])
self.sendLine("NICK " + self.config["nickname"])
self.sendLine("USER " + ident + " * * *")
def handle_disconnect(self):
self.pluginManager.event("disconnect", self.config["network"])
def prnt(self, line):
print(time.strftime("%Y-%m-%d %H:%M:%S") + (" ["+self.config["network"]+"] "+line))
print(time.strftime("%Y-%m-%d %H:%M:%S") + (" [" + self.config["network"] + "] " + line))
def sendLine(self, line):
#print("'"+line+"'")
#print("'" + line + "'")
if line.endswith("\r\n"):
self.push(bytes(line, "utf_8"))
else:
self.push(bytes(line + "\r\n", "utf_8"))
def doMessage(self, channel, message):
self.sendLine("PRIVMSG "+channel+" :"+message)
self.sendLine("PRIVMSG " + channel + " :" + message)
self.pluginManager.event("message", channel, User(self.config["nickname"]), message)
def doAction(self, channel, action):
self.sendLine("PRIVMSG "+channel+" :\x01ACTION "+action+" \x01")
self.sendLine("PRIVMSG " + channel + " :\x01ACTION " + action + " \x01")
self.pluginManager.event("action", channel, User(self.config["nickname"]), action)
def doQuit(self, message=""):
self.sendLine("QUIT :" + message)
self.pluginManager.event("quit", User(self.config["nickname"]), message)
self.close_when_done()
# self.prnt("Closing when done...")
# self.close_when_done()
# self.prnt("sys.exiting...")
# sys.exit("By user request.")
os._exit(2)
self.prnt("Exited.")
def doNotice(self, user, message):
self.sendLine("NOTICE "+user+" :"+message)
self.sendLine("NOTICE " + user + " :" + message)
def doNick(self, newnick):
self.sendLine("NICK " + newnick)
......@@ -391,7 +404,7 @@ class IRC(asynchat.async_chat):
self.config["nickname"] = newnick
def doJoin(self, channel):
self.sendLine("JOIN "+channel)
self.sendLine("JOIN " + channel)
self.pluginManager.event("join", channel, User(self.config["nickname"]))
def doKick(self, channel, user, message=""):
......@@ -399,11 +412,11 @@ class IRC(asynchat.async_chat):
self.pluginManager.event("kick", channel, User(self.config["nickname"]), user, message)
def doPart(self, channel, message=""):
self.sendLine("PART "+channel)
self.sendLine("PART " + channel)
if channel in list(self.userlist.keys()):
del self.userlist[channel]
self.pluginManager.event("part", channel, User(self.config["nickname"]), message)
def doMode(self, channel, mode, user=""):
self.sendLine("MODE "+channel+" "+mode+" "+user)
self.sendLine("MODE " + channel + " " + mode + " " + user)
self.pluginManager.event("mode", channel, User(self.config["nickname"]), mode, user)
......@@ -6,11 +6,13 @@
import os, inspect, collections
pList = { }
pList = {}
mList = collections.defaultdict(list)
class NoSuchPluginError(Exception): pass
class NoSuchPluginError(Exception):
pass
def plugin(cls):
#Using inspect, figure out what file is calling this function (using @plugin)
......@@ -28,7 +30,8 @@ def plugin(cls):
#return cls, since decorators must return the "new" type
return cls
def refresh(pluginName = None):
def refresh(pluginName=None):
"""
Refreshes the list of module/class pairs, and plugin/class pairs.
......@@ -64,7 +67,7 @@ def refresh(pluginName = None):
#for file (module) in the file list
for f in _files:
#Set "plugin" in the environment so that @plugin decorators work correctly
env = {"plugin":plugin}
env = {"plugin": plugin}
#Execute the file. The file will automatically update the list of plugins, due to the @plugin decorator
exec(compile(open(f).read(), f, 'exec'), env, env)
......@@ -83,11 +86,11 @@ def refresh(pluginName = None):
def __reload(pluginName):
#iterate over each "file", "module" pair in mList
for f,classes in mList:
for f, classes in mList:
#if the requested
if pluginName in classes:
#Set "plugin" in the environment so that @plugin decorators work correctly
env = {"plugin":plugin}
env = {"plugin": plugin}
#Execute the file. The file will automatically update the list of plugins, due to the @plugin decorator
exec(compile(open(f).read(), f, 'exec'), env, env)
......@@ -98,6 +101,7 @@ def __reload(pluginName):
#Else, we have not found it, so return False.
return False
def getPlugin(name):
""" Helper function to get a plugin's class. """
try:
......
# guppy Copyright (C) 2010-2011 guppy team members.
# Copyright (C) 2010-2016 Svetlana A. Tkachenko <svetlana@members.fsf.org>
# Copyright (C) 2010-2016 David Vo <david.vo2@gmail.com>
# Copyright (C) 2010-2016 FurryHead <furryhead14@yahoo.com>
#
# This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
# This is free software, and you are welcome to redistribute it
# under certain conditions; type `show c' for details.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# This file is part of guppy.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
# guppy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# guppy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with guppy. If not, see <http://www.gnu.org/licenses/>.
@plugin
......@@ -25,7 +24,7 @@ class AdminTools(object):
def __init__(self, server):
self.server = server
self.server.pluginManager.loadPlugin("auth")
self.commands = [ "nick", "join", "part", "kick", "ban", "mute", "unban", "unmute", "op", "deop", "voice", "devoice", "unop", "unvoice","attrs" ]
self.commands = ["nick", "join", "part", "kick", "ban", "mute", "unban", "unmute", "op", "deop", "voice", "devoice", "unop", "unvoice", "attrs", "die"]
self.server.handle("command", self.handle_command, self.commands)
def handle_command(self, channel, user, cmd, args):
......@@ -53,7 +52,7 @@ class AdminTools(object):
else:
if len(args) < 1:
self.server.doMessage(channel, user+": Not enough arguments.")
self.server.doMessage(channel, user + ": Not enough arguments.")
return
if cmd == "join":
......@@ -79,7 +78,7 @@ class AdminTools(object):
if cmd == "die":
self.server.doMessage(channel, user + " wants me to leave, but I'll be back!")
self.server.doQuit()
elif cmd == "attrs": # attrs. --Kudu.
elif cmd == "attrs": # attrs. --Kudu.
if len(args) == 1:
self.server.doMessage(channel, user + ": " + str(dir(self.server.getPlugin(args[0]))))
elif len(args) == 2:
......
# guppy Copyright (C) 2010-2011 guppy team members.
# Copyright (C) 2010-2016 Svetlana A. Tkachenko <svetlana@members.fsf.org>
# Copyright (C) 2010-2016 David Vo <david.vo2@gmail.com>
# Copyright (C) 2010-2016 FurryHead <furryhead14@yahoo.com>
# Copyright (C) 2011 Kenneth K. Sham <ken@ksham.net>
#
# This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
# This is free software, and you are welcome to redistribute it
# under certain conditions; type `show c' for details.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This file is part of guppy.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# guppy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
# guppy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with guppy. If not, see <http://www.gnu.org/licenses/>.
import os, configparser
......@@ -26,7 +25,7 @@ class Auth(object):
"""Manage user permissions and user data."""
def __init__(self, server):
self.server = server
self.commands = [ "addowner", "delowner", "addadmin", "addmod", "deladmin", "delmod", "admins", "mods", "owners", "passwd", "identify" ]
self.commands = ["addowner", "delowner", "addadmin", "addmod", "deladmin", "delmod", "admins", "mods", "owners", "passwd", "identify"]
self.owners = {}
self.admins = {}
self.mods = {}
......@@ -70,104 +69,107 @@ class Auth(object):
def handle_command(self, channel, user, cmd, args):
if cmd == "owners":
self.server.doMessage(channel, user+": My owners are: "+" ".join(self.owners))
self.server.doMessage(channel, user + ": My owners are: " + " ".join(self.owners))
elif cmd == "admins":
self.server.doMessage(channel, user+": My administrators are: "+" ".join(self.admins))
self.server.doMessage(channel, user + ": My administrators are: " + " ".join(self.admins))
elif cmd == "mods":
self.server.doMessage(channel, user+": My moderators are: "+" ".join(self.mods))
self.server.doMessage(channel, user + ": My moderators are: " + " ".join(self.mods))
elif cmd == "passwd":
if channel == user:
if user.lower() in list(self.owners.keys()):
if self.owners[user.lower()][1]:
if len(args) < 1:
self.server.doMessage(channel, user+": Not enough arguments.")
self.server.doMessage(channel, user + ": Not enough arguments.")
return
if len(args) >= 2:
self.owners[user.lower()][0] = args[1]
self.server.doMessage(channel, user+": Password successfully changed.")
self.server.doMessage(channel, user + ": Password successfully changed.")
else:
self.owners[user.lower()][0] = args[0]
self.server.doMessage(channel, user+": Password successfully changed.")
self.server.doMessage(channel, user + ": Password successfully changed.")
elif user.lower() in list(self.admins.keys()):
if self.admins[user.lower()][1]:
if len(args) < 1:
self.server.doMessage(channel, user+": Not enough arguments.")
self.server.doMessage(channel, user + ": Not enough arguments.")
return
if len(args) >= 2:
self.admins[user.lower()][0] = args[1]
self.server.doMessage(channel, user+": Password successfully changed.")
self.server.doMessage(channel, user + ": Password successfully changed.")
else:
self.admins[user.lower()][0] = args[0]
self.server.doMessage(channel, user+": Password successfully changed.")
self.server.doMessage(channel, user + ": Password successfully changed.")
elif user.lower() in list(self.mods.keys()):
if self.mods[user.lower()][1]:
if len(args) < 1:
self.server.doMessage(channel, user+": Not enough arguments.")
self.server.doMessage(channel, user + ": Not enough arguments.")
return
if len(args) >= 2:
self.mods[user.lower()][0] = args[1]
self.server.doMessage(channel, user+": Password successfully changed.")
self.server.doMessage(channel, user + ": Password successfully changed.")
else:
self.mods[user.lower()][0] = args[0]
self.server.doMessage(channel, user+": Password successfully changed.")
self.server.doMessage(channel, user + ": Password successfully changed.")
else:
self.server.doMessage(channel, user+": You are not in the owners/admins/mods lists.")
self.server.doMessage(channel, user + ": You are not in the owners/admins/mods lists.")
return
else:
self.server.doMessage(channel, user+": Please do that using private messaging.")
self.server.doMessage(channel, user + ": Please do that using private messaging.")
return
elif cmd == "identify":
if len(args) < 1:
self.server.doMessage(channel, user+": Not enough arguments.")
self.server.doMessage(channel, user + ": Not enough arguments.")
return
if channel == user:
if user.lower() in list(self.owners.keys()):
if self.owners[user.lower()][1]:
self.server.doMessage(channel, user+": You are already identified.")
self.server.doMessage(channel, user + ": You are already identified.")
elif args[0] == self.owners[user.lower()][0]:
self.owners[user.lower()][1] = True
self.server.doMessage(channel, user+": You are now identified for "+user)
self.server.doMessage(channel, user + ": You are now identified for " + user)
else:
self.server.doMessage(channel, user+": Invalid password.")
self.server.doMessage(channel, user + ": Invalid password.")
elif user.lower() in list(self.admins.keys()):
if self.admins[user.lower()][1]:
self.server.doMessage(channel, user+": You are already identified.")
self.server.doMessage(channel, user + ": You are already identified.")
elif args[0] == self.admins[user.lower()][0]:
self.admins[user.lower()][1] = True
self.server.doMessage(channel, user+": You are now identified for "+user)
self.server.doMessage(channel, user + ": You are now identified for " + user)
else:
self.server.doMessage(channel, user+": Invalid password.")
self.server.doMessage(channel, user + ": Invalid password.")
elif user.lower() in list(self.mods.keys()):
if self.mods[user.lower()][1]:
self.server.doMessage(channel, user+": You are already identified.")
self.server.doMessage(channel, user + ": You are already identified.")
elif args[0] == self.mods[user.lower()][0]:
self.mods[user.lower()][1] = True
self.server.doMessage(channel, user+": You are now identified for "+user)
self.server.doMessage(channel, user + ": You are now identified for " + user)
else:
self.server.doMessage(channel, user+": Invalid password.")
self.server.doMessage(channel, user + ": Invalid password.")
else:
self.server.doMessage(channel, user+": Please do that using private messaging.")
self.server.doMessage(channel, user + ": Please do that using private messaging.")
return
elif self.isOwner(user):
if len(args) < 1:
self.server.doMessage(channel, user+": What nick?")
self.server.doMessage(channel, user + ": What nick?")
return
if cmd == "addowner":
if args[0].lower() in list(self.owners.keys()): return
if args[0].lower() in list(self.owners.keys()):
return
self.owners[args[0].lower()] = ['', True]
elif cmd == "addadmin":
if args[0].lower() in list(self.admins.keys()): return
if args[0].lower() in list(self.admins.keys()):
return
self.admins[args[0].lower()] = ['', True]
elif cmd == "addmod":
if args[0].lower() in list(self.mods.keys()): return
if args[0].lower() in list(self.mods.keys()):
return
self.mods[args[0].lower()] = ['', True]
elif cmd == "delowner":
if args[0].lower() in list(self.owners.keys()):
......@@ -191,9 +193,9 @@ class Auth(object):
if not self.configParser.has_section(network):
self.configParser.add_section(network)
self.configParser.set(self.server.config["network"],"owners",",".join( [k+":"+v[0] for k,v in list(self.owners.items())] ))
self.configParser.set(self.server.config["network"],"admins",",".join( [k+":"+v[0] for k,v in list(self.admins.items())] ))
self.configParser.set(self.server.config["network"],"mods",",".join( [k+":"+v[0] for k,v in list(self.mods.items())] ))
self.configParser.set(self.server.config["network"], "owners", ",".join([k + ":" + v[0] for k, v in list(self.owners.items())]))
self.configParser.set(self.server.config["network"], "admins", ",".join([k + ":" + v[0] for k, v in list(self.admins.items())]))
self.configParser.set(self.server.config["network"], "mods", ",".join([k + ":" + v[0] for k, v in list(self.mods.items())]))
self.configParser.write(fh)
fh.close()
......@@ -247,4 +249,3 @@ class Auth(object):
return True
else:
return self.isOwner(user)