Compare commits

..

57 commits

Author SHA1 Message Date
ChChBot admin
358b415566 added corridor light 2017-11-30 22:24:15 +01:00
ChChBot admin
3e1ea6ac4e fixed command names 2017-03-13 23:46:44 +01:00
ChChBot admin
48b627ad4b Merge branch 'ChaosChemnitz' of https://git.chch.it/ChCh/CloudBot_github into ChaosChemnitz 2017-03-13 23:32:29 +01:00
ChChBot admin
1f1ff649c9 deactivated unused functions 2017-03-13 23:31:52 +01:00
8344fe7693 implemented light commands for e-lab 2017-03-13 23:22:15 +01:00
a6992d5d1a Merge branch 'ChaosChemnitz' of vcup.florz.net:/home/cloudbot/CloudBot into ChaosChemnitz 2017-02-24 22:21:33 +01:00
ChChBot admin
21db4efd98 implemented lounge_light command 2017-02-24 22:20:30 +01:00
ChChBot admin
8941d59dc2 removed buggy part for killing old processes 2017-02-24 17:08:01 +01:00
09ce708709 Merge branch 'ChaosChemnitz' of vcup.florz.net:/home/cloudbot/CloudBot into ChaosChemnitz 2017-02-23 00:46:13 +01:00
ChChBot admin
29cc95f0ad Merge branch 'ChaosChemnitz' of https://git.chch.it/ChCh/CloudBot_github into ChaosChemnitz
Conflicts:
	plugins/chch_worker.py
2017-02-23 00:41:05 +01:00
4a02f545ea Merge branch 'ChaosChemnitz' of github.com:ChaosChemnitz/CloudBot into ChaosChemnitz 2017-02-23 00:35:13 +01:00
ChChBot admin
28176ad2ad added new feature to toggle lounge light 2017-02-23 00:18:23 +01:00
ChChBot admin
651d4a953e fork and disown start script 2017-02-22 23:45:52 +01:00
ChChBot admin
80711e1278 changed IP to localhost 2017-02-22 23:45:32 +01:00
a88d5ef0bd Merge pull request #4 from marenz2569/ChaosChemnitz
command lamp_lounge: UDP -> TCP
2017-01-12 10:17:45 +01:00
827c5c73e8 command lamp_lounge: UDP -> TCP 2017-01-11 21:29:01 +01:00
ChChBot admin
580415ed76 Merge branch 'ChaosChemnitz' of https://git.chch.it/ChCh/CloudBot_github into ChaosChemnitz 2016-11-17 21:40:48 +01:00
c562a2c7ae Merge branch 'ChaosChemnitz' of github.com:ChaosChemnitz/CloudBot into ChaosChemnitz 2016-11-17 21:32:54 +01:00
ChChBot admin
4b5e86c237 ignore config.ssl 2016-11-17 21:24:07 +01:00
txt.file
11687a9ad0 Merge pull request #3 from marenz2569/ChaosChemnitz
Fensterbogenbeleuchtungssteuerung
2016-11-17 19:18:48 +00:00
296de07e14 uncommented event 332 2016-11-17 19:53:00 +01:00
dfbead6946 added command for controlling the fensterbogenbeleuchtung in the lounge (lamp_lounge) 2016-11-17 19:50:06 +01:00
root
e30eca5ebd old changes + fixed utf8 problems 2016-02-04 16:55:03 +01:00
root
2a3c9f52dc added logging to syslog 2016-02-04 16:53:15 +01:00
ChChBot admin
f88c3267d4 created reverse SSH tunnel for light control 2015-02-21 20:11:56 +01:00
ChChBot admin
2b18eaac5a Revert "disable chch_worker"
This reverts commit 05bfb9099a.
2015-02-20 13:13:45 +01:00
0650046d18 Merge branch 'ChaosChemnitz' of https://github.com/Stummi/CloudBot into ChaosChemnitz 2015-02-19 22:14:13 +01:00
Michael Stummvoll
4e386c9008 update default config 2015-02-19 17:22:27 +01:00
Michael Stummvoll
05bfb9099a disable chch_worker 2015-02-19 17:20:59 +01:00
Michael Stummvoll
bfa3fb6e74 move version check into own plugin 2015-02-19 17:20:35 +01:00
Florian Schlegel
d415d0fc69 Merge pull request #2 from ChaosChemnitz/revert-1-ChaosChemnitz
Revert "add ctcp version check and kick the logbot clients"
2015-02-19 16:22:32 +01:00
Florian Schlegel
433d97d276 Revert "add ctcp version check and kick the logbot clients" 2015-02-19 16:21:14 +01:00
Florian Schlegel
121715115a Merge pull request #1 from Stummi/ChaosChemnitz
add ctcp version check and kick the logbot clients
2015-02-19 14:06:57 +01:00
Michael Stummvoll
620de651ce add ctcp version check and kick the logbot clients 2015-02-19 13:59:27 +01:00
ChChBot admin
3f205eaafa simple restart script to keep the bot alive 2015-02-18 23:01:52 +01:00
ChChBot admin
59cd8380d2 some changes... 2015-02-18 22:55:28 +01:00
ChChBot admin
7cce9bf27e disabled many plugins 2015-02-18 22:53:13 +01:00
ChChBot admin
0ba2001b62 some new plugins (thx to _20h_) 2015-02-18 22:49:44 +01:00
Morris Jobke
63fc042027 add plugin for topic update with status 2014-04-05 12:27:28 +02:00
Luke Rogers
9421d8160d Merge pull request #224 from daboross/patch-3
Create config.default that was removed
2014-04-04 14:11:33 +13:00
Dabo Ross
4b4ac2d918 Create config.default that was removed
1062e69e56 ?
2014-04-03 09:29:09 -07:00
Luke Rogers
05a68faf1d tidied 2014-04-03 19:44:38 +13:00
Luke Rogers
40328cf24f Merge remote-tracking branch 'origin/develop' into develop 2014-04-03 18:27:42 +13:00
Luke Rogers
fb471bee17 Added random output, switched API URL 2014-04-03 18:27:17 +13:00
Luke Rogers
48ab7417b8 Update recipe.py 2014-04-03 15:01:19 +13:00
Luke Rogers
97a3283eff Switched suggest API 2014-04-02 23:22:39 +13:00
Luke Rogers
6d147e1677 Stuff 2014-04-02 20:12:21 +13:00
Luke Rogers
c99c44616c Adapted code to get multiple results! 2014-04-02 20:08:56 +13:00
Luke Rogers
1062e69e56 Strip spaces 2014-04-02 20:03:14 +13:00
Luke Rogers
5ca45fea4b Added recipe searching! 2014-04-02 15:03:10 +13:00
Luke Rogers
04af154c5b Added more features! 2014-04-01 19:43:03 +13:00
Luke Rogers
f82041fc87 Merge branch 'develop' of https://github.com/ClouDev/CloudBot into develop 2014-04-01 17:58:00 +13:00
Luke Rogers
a027bd780f Added fancy new recipe plugin! 2014-04-01 17:57:39 +13:00
Luke Rogers
a611e0df45 Merge pull request #221 from daboross/fix-dictionary-unicode-support
Fix unicode support for dictionary.py
2014-03-31 20:58:10 +13:00
Luke Rogers
e49ec9a9c9 Merge pull request #218 from daboross/fix-wiki-unicode-support
Fix unicode support for wikipedia
2014-03-31 20:58:09 +13:00
Dabo Ross
4652ed90a3 Fix unicode support for wikipedia 2014-03-28 18:15:39 -07:00
Dabo Ross
ff3ef576b7 Fix unicode support for dictionary.py 2014-03-27 10:30:30 -07:00
232 changed files with 21345 additions and 1396 deletions

9
.gitignore vendored
View file

@ -1,5 +1,6 @@
persist
config.json
config
config.ssl
gitflow
*.db
*.log
@ -11,8 +12,4 @@ gitflow
*.sublime-project
*.sublime-workspace
.idea/
data/GeoLiteCity.dat
plugins/New Text Document.txt
plugins/srvv.py
run.cmd
config
plugins/data/GeoLiteCity.dat

View file

@ -14,37 +14,33 @@ Unzip the resulting file, and continue to read this document.
### Install
Before you can run the bot, you need to install a few Python dependencies. LXML is required while Enchant, PyGeoIP, TweePy and PyDNS are needed for several plugins.
Before you can run the bot, you need to install a few Python dependencies. LXML is required while Enchant and PyDNS are needed for several plugins.
These can be installed with `pip` (The Python package manager) by running the following command in the bot directory:
These can be installed with `pip` (The Python package manager):
pip install -r requirements.txt
[sudo] pip install -r requirements.txt
**Note:** If you use `pip`, you will also need the following packages on linux or `pip` will fail to install the requirements.
If you use `pip`, you will also need the following packages on linux or `pip` will fail to install the requirements.
```python, python-dev, libenchant-dev, libenchant1c2a, libxslt-dev, libxml2-dev.```
(this can be done using your package manager (eg: *apt-get* or *yum*)
#### How to install `pip`
You can usually install pip on linux by installing the `python-pip` package using your package manager (eg. *apt-get install python-pip* or *yum install python-pip* as root), or you can try the below code to download and install it manually.
curl -O http://python-distribute.org/distribute_setup.py # or download with your browser on windows
python distribute_setup.py
easy_install pip
If you need help installing pip on Windows, follow [this guide](http://simpledeveloper.com/how-to-install-easy_install/) and then run `easy_install pip` on the command line.
If you are unable to use pip, there are Windows installers for LXML available for [64 bit](https://pypi.python.org/packages/2.7/l/lxml/lxml-2.3.win-amd64-py2.7.exe) and [32 bit](https://pypi.python.org/packages/2.7/l/lxml/lxml-2.3.win32-py2.7.exe) versions of Python.
### Run
Before you run the bot, rename `config.default` to `config.json` and edit it with your preferred settings. You can check if your JSON is valid on [this site](http://jsonlint.com/)!
Before you run the bot, rename `config.default` to `config` and edit it with your preferred settings.
Once you have installed the required dependencies and renamed the config file, you can run the bot! Make sure you are in the correct folder and run the following command:
`python cloudbot.py`
`python bot.py`
On Windows you can usually just double-click `cloudbot.py` to start the bot, as long as you have Python installed correctly.
On Windows you can usually just double-click `bot.py` to start the bot, as long as you have Python installed correctly.
## Getting help with CloudBot
@ -62,17 +58,15 @@ More at the [Wiki Main Page](http://git.io/cloudbotircwiki).
The developers reside in [#CloudBot](irc://irc.esper.net/cloudbot) on [EsperNet](http://esper.net) and would be glad to help you.
If you think you have found a bug/have a idea/suggestion, please **open a issue** here on Github and contact us on IRC!
If you think you have found a bug/have a idea/suggestion, please **open a issue** here on Github.
### Requirements
CloudBot runs on **Python** *2.7.x*. It is currently developed on **Windows** *8* with **Python** *2.7.5*.
It **requires the Python modules** lXML, watchdog and BeautifulSoup4.
It **requires the Python module** lXML.
The module `Enchant` is needed for the spellcheck plugin.
The module `PyDNS` is needed for SRV record lookup in the mcping plugin.
The module `PyGeoIP` is needed for location lookup in the geoip plugin.
The module `TweePy` is needed for the twitter plugin.
**Windows** users: Windows compatibility some plugins is **broken** (such as ping), but we do intend to add it. Eventually.

View file

@ -1,52 +1,74 @@
#!/usr/bin/env python
from core import bot
import os
import Queue
import sys
import time
import signal
import re
# check python version
if sys.version_info < (3, 2, 0):
print("CloudBot3 requires Python 3.2 or newer.")
sys.exit(1)
# set up environment
sys.path += ['plugins', 'lib'] # add stuff to the sys.path for easy imports
os.chdir(sys.path[0] or '.') # do stuff relative to the install directory
# this is not the code you are looking for
if os.path.exists(os.path.abspath('lib')):
sys.path += ['lib']
print('CloudBot3 <http://git.io/cloudbotirc>')
class Bot(object):
pass
print 'CloudBot DEV <http://git.io/cloudbotirc>'
def exit_gracefully(signum, frame):
# this doesn't really work at all
cloudbot.stop()
# create new bot object
bot = Bot()
bot.vars = {}
# restore the original handler so if they do it again it triggers
signal.signal(signal.SIGINT, original_sigint)
# record start time for the uptime command
bot.start_time = time.time()
# store the original SIGINT handler
original_sigint = signal.getsignal(signal.SIGINT)
signal.signal(signal.SIGINT, exit_gracefully)
print 'Begin Plugin Loading.'
# create a bot master and start it
cloudbot = bot.CloudBot()
cloudbot.start()
# bootstrap the reloader
eval(compile(open(os.path.join('core', 'reload.py'), 'U').read(),
os.path.join('core', 'reload.py'), 'exec'))
reload(init=True)
# watch to see if the bot stops running or needs a restart
while True:
if cloudbot.running:
time.sleep(.1)
else:
if cloudbot.do_restart:
# create a new bot thread and start it
# Todo: Make this work
del cloudbot
cloudbot = bot.Bot()
cloudbot.start()
continue
config()
if not hasattr(bot, 'config'):
exit()
print 'Connecting to IRC...'
bot.conns = {}
try:
for name, conf in bot.config['connections'].iteritems():
# strip all spaces and capitalization from the connection name
name = name.replace(" ", "_")
name = re.sub('[^A-Za-z0-9_]+', '', name)
print 'Connecting to server: %s' % conf['server']
if conf.get('ssl'):
bot.conns[name] = SSLIRC(name, conf['server'], conf['nick'], conf=conf,
port=conf.get('port', 6667), channels=conf['channels'],
ignore_certificate_errors=conf.get('ignore_cert', True))
else:
break
bot.conns[name] = IRC(name, conf['server'], conf['nick'], conf=conf,
port=conf.get('port', 6667), channels=conf['channels'])
except Exception as e:
print 'ERROR: malformed config file', e
sys.exit()
bot.persist_dir = os.path.abspath('persist')
if not os.path.exists(bot.persist_dir):
os.mkdir(bot.persist_dir)
print 'Connection(s) made, starting main loop.'
while True:
reload() # these functions only do things
config() # if changes have occured
for conn in bot.conns.itervalues():
try:
out = conn.out.get_nowait()
main(conn, out)
except Queue.Empty:
pass
while all(conn.out.empty() for conn in bot.conns.itervalues()):
time.sleep(.1)

View file

@ -1,62 +1,77 @@
{
"connections":
[
{
"name": "esper",
"connection": {
"server": "irc.esper.net",
"port": 6667,
"ssl": false,
"ignore_cert": true
},
"nick": "MyCloueqerdBot",
"user": "cloudbot",
"real_name": "CloudBot - http://git.io/cloudbotirc",
"channels": ["#cloudbot", "#cloudbot2"],
"disabled_commands": [],
"acls": {},
"nickserv": {
"enabled": false,
"nickserv_password": "",
"nickserv_user": "",
"nickserv_name": "nickserv",
"nickserv_command": "IDENTIFY"
},
"permissions": {
"connections": {
"hackint": {
"server": "irc.hackint.eu",
"nick": "antibot",
"user": "antibot",
"realname": "CloudBot - http://git.io/cloudbotirc",
"mode": "",
"_nickserv_password": "",
"-nickserv_user": "",
"channels": [
"#ChaosChemnitz",
"#logbot"
],
"invite_join": true,
"auto_rejoin": false,
"command_prefix": "."
}
},
"disabled_plugins": [],
"disabled_commands": [],
"acls": {},
"api_keys": {
"tvdb": "",
"wolframalpha": "",
"lastfm": "",
"rottentomatoes": "",
"soundcloud": "",
"twitter_consumer_key": "",
"twitter_consumer_secret": "",
"twitter_access_token": "",
"twitter_access_secret": "",
"wunderground": "",
"googletranslate": "",
"rdio_key": "",
"rdio_secret": ""
},
"permissions": {
"admins": {
"perms": ["adminonly", "addfactoid", "delfactoid", "ignore", "botcontrol", "permissions_users", "op"],
"users": ["examplea!user@example.com", "exampleb!user@example.com"]
"perms": [
"adminonly",
"addfactoid",
"delfactoid",
"ignore",
"botcontrol",
"permissions_users",
"op"
],
"users": [
"examplea!user@example.com",
"exampleb!user@example.com"
]
},
"moderators": {
"perms": ["addfactoid", "delfactoid", "ignore"],
"users": ["examplec!user@example.com"]
},
"trusted": {
"perms": ["addfactoid", "delfactoid"],
"users": ["exampled!user@example.com"]
"perms": [
"addfactoid",
"delfactoid",
"ignore"
],
"users": [
"stummi!~Stummi@stummi.org"
]
}
},
"plugins": {
},
"command_prefix": "."
}
],
"api_keys":
{
"tvdb": "",
"wolframalpha": "",
"lastfm": "",
"rottentomatoes": "",
"soundcloud": "",
"twitter_consumer_key": "",
"twitter_consumer_secret": "",
"twitter_access_token": "",
"twitter_access_secret": "",
"wunderground": "",
"googletranslate": "",
"rdio_key": "",
"rdio_secret": ""
},
"disabled_plugins": []
},
"plugins": {
"factoids": {
"prefix": false
},
"ignore": {
"ignored": []
}
},
"censored_strings": [
"mypass",
"mysecret"
]
}

View file

@ -1,175 +0,0 @@
import time
import logging
import re
import os
import queue
import collections
import threading
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy import create_engine
from core import config, irc, main
from core.permissions import PermissionManager
from core.loader import PluginLoader
def clean_name(n):
"""strip all spaces and capitalization"""
return re.sub('[^A-Za-z0-9_]+', '', n.replace(" ", "_"))
def get_logger():
"""create and return a new logger object"""
# create logger
logger = logging.getLogger("cloudbot")
logger.setLevel(logging.DEBUG)
# add a file handler
log_name = "bot.log"
fh = logging.FileHandler(log_name)
fh.setLevel(logging.INFO)
# stdout handler
sh = logging.StreamHandler()
sh.setLevel(logging.DEBUG)
# create a formatter and set the formatter for the handler.
frmt = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s')
fh.setFormatter(frmt)
simple_frmt = logging.Formatter('[%(levelname)s] %(message)s')
sh.setFormatter(simple_frmt)
# add the Handlers to the logger
logger.addHandler(fh)
logger.addHandler(sh)
return logger
class CloudBot(threading.Thread):
def __init__(self):
# basic variables
self.start_time = time.time()
self.running = True
self.do_restart = False
# stores each instance of the
self.instances = []
# set up config and logging
self.setup()
self.logger.debug("Bot setup completed.")
# start bot instances
self.create()
for instance in self.instances:
instance.permissions = PermissionManager(self, instance)
# run plugin loader
self.plugins = collections.defaultdict(list)
""" self.plugins format
{'PLUGIN_TYPE': [(<COMPILED_PLUGIN_FUNTION>,
{PLUGIN_ARGS}),
(<COMPILED_PLUGIN_FUNTION>,
{PLUGIN_ARGS})],
'PLUGIN_TYPE': [(<COMPILED_PLUGIN_FUNTION>,
{PLUGIN_ARGS})]
}
"""
self.threads = {}
self.loader = PluginLoader(self)
threading.Thread.__init__(self)
def run(self):
"""recieves input from the IRC engine and processes it"""
self.logger.info("Starting main thread.")
while self.running:
for instance in self.instances:
try:
incoming = instance.parsed_queue.get_nowait()
if incoming == StopIteration:
print("StopIteration")
# IRC engine has signalled timeout, so reconnect (ugly)
instance.connection.reconnect()
main.main(self, instance, incoming)
except queue.Empty:
pass
# if no messages are in the incoming queue, sleep
while self.running and all(i.parsed_queue.empty() for i in self.instances):
time.sleep(.1)
def setup(self):
"""create the logger and config objects"""
# logging
self.logger = get_logger()
self.logger.debug("Logging system initalised.")
# data folder
self.data_dir = os.path.abspath('persist')
if not os.path.exists(self.data_dir):
self.logger.debug("Data folder not found, creating.")
os.mkdir(self.data_dir)
# config
self.config = config.Config(self)
self.logger.debug("Config system initalised.")
# db
engine = create_engine('sqlite:///cloudbot.db')
db_factory = sessionmaker(bind=engine)
self.db_session = scoped_session(db_factory)
self.logger.debug("Database system initalised.")
def create(self):
""" Create a BotInstance for all the networks defined in the config """
for conf in self.config['instances']:
# strip all spaces and capitalization from the connection name
name = clean_name(conf['name'])
nick = conf['nick']
server = conf['connection']['server']
port = conf['connection'].get('port', 6667)
self.logger.debug("Creating BotInstance for {}.".format(name))
self.instances.append(irc.BotInstance(name, server, nick, config=conf,
port=port, logger=self.logger, channels=conf['channels'],
ssl=conf['connection'].get('ssl', False)))
self.logger.debug("({}) Created connection.".format(name))
def stop(self, reason=None):
"""quits all networks and shuts the bot down"""
self.logger.info("Stopping bot.")
self.config.observer.stop()
self.logger.debug("Stopping config reloader.")
self.loader.stop()
self.logger.debug("Stopping plugin loader.")
for connection in self.connections:
self.logger.debug("({}) Closing connection.".format(connection.name))
if reason:
connection.cmd("QUIT", [reason])
else:
connection.cmd("QUIT")
connection.stop()
self.logger.debug("Logging engine stopped")
logging.shutdown()
self.running = False
def restart(self, reason=None):
"""shuts the bot down and restarts it"""
self.do_restart = True
self.stop(reason)

View file

@ -1,69 +1,27 @@
import inspect
import json
import os
import time
import sys
from watchdog.observers import Observer
from watchdog.tricks import Trick
class Config(dict):
def __init__(self, bot, *args, **kwargs):
self.filename = "config.json"
self.path = os.path.abspath(self.filename)
self.bot = bot
self.logger = bot.logger
self.update(*args, **kwargs)
def save(conf):
json.dump(conf, open('config', 'w'), sort_keys=True, indent=2)
# populate self with config data
self.load_config()
# start watcher
self.watcher()
if not os.path.exists('config'):
print "Please rename 'config.default' to 'config' to set up your bot!"
print "For help, see http://git.io/cloudbotirc"
print "Thank you for using CloudBot!"
sys.exit()
def load_config(self):
"""(re)loads the bot config from the config file"""
if not os.path.exists(self.path):
# if there is no config, show an error and die
self.logger.critical("No config file found, bot shutting down!")
print("No config file found! Bot shutting down in five seconds.")
print("Copy 'config.default' to 'config.json' for defaults.")
print("For help, see http://git.io/cloudbotirc. Thank you for using CloudBot!")
time.sleep(5)
sys.exit()
def config():
# reload config from file if file has changed
config_mtime = os.stat('config').st_mtime
if bot._config_mtime != config_mtime:
try:
bot.config = json.load(open('config'))
bot._config_mtime = config_mtime
except ValueError, e:
print 'error: malformed config', e
with open(self.path) as f:
self.update(json.load(f))
self.logger.info("Config loaded from file.")
# reload permissions
if self.bot.instances:
for instance in self.bot.instances:
instance.permissions.reload()
def save_config(self):
"""saves the contents of the config dict to the config file"""
json.dump(self, open(self.path, 'w'), sort_keys=True, indent=2)
self.logger.info("Config saved to file.")
def watcher(self):
"""starts the watchdog to automatically reload the config when it changes on disk"""
self.observer = Observer()
pattern = "*{}".format(self.filename)
self.event_handler = ConfigEventHandler(self, patterns=[pattern])
self.observer.schedule(self.event_handler, path='.', recursive=False)
self.observer.start()
class ConfigEventHandler(Trick):
def __init__(self, config, *args, **kwargs):
self.config = config
self.logger = config.logger
Trick.__init__(self, *args, **kwargs)
def on_any_event(self, event):
self.logger.info("Config changed, triggering reload.")
self.config.load_config()
bot._config_mtime = 0

View file

@ -1,6 +1,6 @@
import os
import sqlite3
import _thread
import thread
threaddbs = {}
@ -11,10 +11,10 @@ def get_db_connection(conn, name=''):
if not name:
name = '{}.db'.format(conn.name)
threadid = _thread.get_ident()
threadid = thread.get_ident()
if name in threaddbs and threadid in threaddbs[name]:
return threaddbs[name][threadid]
filename = os.path.join(bot.data_dir, name)
filename = os.path.join(bot.persist_dir, name)
db = sqlite3.connect(filename, timeout=10)
if name in threaddbs:

View file

@ -1,18 +1,11 @@
import re
import socket
import time
import threading
import queue
from core import permissions
import thread
import Queue
from ssl import wrap_socket, CERT_NONE, CERT_REQUIRED, SSLError
irc_prefix_rem = re.compile(r'(.*?) (.*?) (.*)').match
irc_noprefix_rem = re.compile(r'()(.*?) (.*)').match
irc_netmask_rem = re.compile(r':?([^!@]*)!?([^@]*)@?(.*)').match
irc_param_ref = re.compile(r'(?:^|(?<= ))(:.*|[^ ]+)').findall
def decode(txt):
for codec in ('utf-8', 'iso-8859-1', 'shift_jis', 'cp1252'):
@ -24,44 +17,78 @@ def decode(txt):
def censor(text):
text = text.replace('\n', '').replace('\r', '')
replacement = '[censored]'
if 'censored_strings' in bot.config:
if bot.config['censored_strings']:
words = map(re.escape, bot.config['censored_strings'])
regex = re.compile('({})'.format("|".join(words)))
text = regex.sub(replacement, text)
return text
class ReceiveThread(threading.Thread):
"""receives messages from IRC and puts them in the input_queue"""
def __init__(self, sock, input_queue, timeout):
self.input_buffer = b""
self.input_queue = input_queue
self.socket = sock
class crlf_tcp(object):
"""Handles tcp connections that consist of utf-8 lines ending with crlf"""
def __init__(self, host, port, timeout=300):
self.ibuffer = ""
self.obuffer = ""
self.oqueue = Queue.Queue() # lines to be sent out
self.iqueue = Queue.Queue() # lines that were received
self.socket = self.create_socket()
self.host = host
self.port = port
self.timeout = timeout
self.shutdown = False
threading.Thread.__init__(self)
def create_socket(self):
return socket.socket(socket.AF_INET, socket.TCP_NODELAY)
def run(self):
noerror = 0
while 1:
try:
self.socket.connect((self.host, self.port))
break
except socket.gaierror as e:
time.sleep(5)
except socket.timeout as e:
time.sleep(5)
thread.start_new_thread(self.recv_loop, ())
thread.start_new_thread(self.send_loop, ())
def recv_from_socket(self, nbytes):
return self.socket.recv(nbytes)
def get_timeout_exception_type(self):
return socket.timeout
def handle_receive_exception(self, error, last_timestamp):
print("Receive exception: %s" % (error))
if time.time() - last_timestamp > self.timeout:
self.input_queue.put(StopIteration)
print("Receive timeout. Restart connection.")
self.iqueue.put(StopIteration)
self.socket.close()
return True
return False
def get_timeout_exception_type(self):
return socket.timeout
def handle_send_exception(self, error):
print("Send exception: %s" % (error))
self.iqueue.put(StopIteration)
self.socket.close()
return True
def run(self):
def recv_loop(self):
last_timestamp = time.time()
while not self.shutdown:
while True:
try:
data = self.recv_from_socket(4096)
self.input_buffer += data
data = self.recv_from_socket(4096)
self.ibuffer += data
if data:
last_timestamp = time.time()
else:
if time.time() - last_timestamp > self.timeout:
self.input_queue.put(StopIteration)
self.iqueue.put(StopIteration)
self.socket.close()
return
time.sleep(1)
@ -69,16 +96,40 @@ class ReceiveThread(threading.Thread):
if self.handle_receive_exception(e, last_timestamp):
return
continue
except AttributeError:
return
while b'\r\n' in self.input_buffer:
line, self.input_buffer = self.input_buffer.split(b'\r\n', 1)
print(decode(line))
self.input_queue.put(decode(line))
while '\r\n' in self.ibuffer:
line, self.ibuffer = self.ibuffer.split('\r\n', 1)
self.iqueue.put(decode(line))
def send_loop(self):
while True:
try:
line = self.oqueue.get().splitlines()[0][:500]
if line == StopIteration:
return
print ">>> %r" % line
self.obuffer += line.encode('utf-8', 'replace') + '\r\n'
while self.obuffer:
sent = self.socket.send(self.obuffer)
self.obuffer = self.obuffer[sent:]
class SSLReceiveThread(ReceiveThread):
def __init__(self, sock, input_queue, timeout):
ReceiveThread.__init__(self, sock, input_queue, timeout)
except socket.error as e:
self.handle_send_exception(e)
return
class crlf_ssl_tcp(crlf_tcp):
"""Handles ssl tcp connetions that consist of utf-8 lines ending with crlf"""
def __init__(self, host, port, ignore_cert_errors, timeout=300):
self.ignore_cert_errors = ignore_cert_errors
crlf_tcp.__init__(self, host, port, timeout)
def create_socket(self):
return wrap_socket(crlf_tcp.create_socket(self), server_side=False,
cert_reqs=CERT_NONE if self.ignore_cert_errors else
CERT_REQUIRED)
def recv_from_socket(self, nbytes):
return self.socket.read(nbytes)
@ -87,50 +138,60 @@ class SSLReceiveThread(ReceiveThread):
return SSLError
def handle_receive_exception(self, error, last_timestamp):
# this is terrible
if not "timed out" in error.args[0]:
raise
return ReceiveThread.handle_receive_exception(self, error, last_timestamp)
# this is terrible
#if not "timed out" in error.args[0]:
# raise
return crlf_tcp.handle_receive_exception(self, error, last_timestamp)
def handle_send_exception(self, error):
return crlf_tcp.handle_send_exception(self, error)
class SendThread(threading.Thread):
"""sends messages from output_queue to IRC"""
def __init__(self, sock, conn_name, output_queue):
self.output_buffer = b""
self.output_queue = output_queue
self.conn_name = conn_name
self.socket = sock
self.shutdown = False
threading.Thread.__init__(self)
def run(self):
while not self.shutdown:
line = self.output_queue.get().splitlines()[0][:500]
self.output_buffer += line.encode('utf-8', 'replace') + b'\r\n'
while self.output_buffer:
sent = self.socket.send(self.output_buffer)
self.output_buffer = self.output_buffer[sent:]
irc_prefix_rem = re.compile(r'(.*?) (.*?) (.*)').match
irc_noprefix_rem = re.compile(r'()(.*?) (.*)').match
irc_netmask_rem = re.compile(r':?([^!@]*)!?([^@]*)@?(.*)').match
irc_param_ref = re.compile(r'(?:^|(?<= ))(:.*|[^ ]+)').findall
class ParseThread(threading.Thread):
"""parses messages from input_queue and puts them in parsed_queue"""
def __init__(self, input_queue, output_queue, parsed_queue):
self.input_queue = input_queue # lines that were received
self.output_queue = output_queue # lines to be sent out
self.parsed_queue = parsed_queue # lines that have been parsed
class IRC(object):
"""handles the IRC protocol"""
threading.Thread.__init__(self)
def __init__(self, name, server, nick, port=6667, channels=[], conf={}):
self.name = name
self.channels = channels
self.conf = conf
self.server = server
self.port = port
self.nick = nick
self.history = {}
self.vars = {}
def run(self):
self.out = Queue.Queue() # responses from the server are placed here
# format: [rawline, prefix, command, params,
# nick, user, host, paramlist, msg]
self.connect()
thread.start_new_thread(self.parse_loop, ())
def create_connection(self):
return crlf_tcp(self.server, self.port)
def connect(self):
self.conn = self.create_connection()
thread.start_new_thread(self.conn.run, ())
self.set_pass(self.conf.get('server_password'))
self.set_nick(self.nick)
self.cmd("USER",
[conf.get('user', 'cloudbot'), "3", "*", conf.get('realname',
'CloudBot - http://git.io/cloudbot')])
def parse_loop(self):
while True:
# get a message from the input queue
msg = self.input_queue.get()
msg = self.conn.iqueue.get()
if msg == StopIteration:
# got a StopIteration from the receive thread, pass it on
# so the main thread can restart the connection
self.parsed_queue.put(StopIteration)
self.connect()
continue
# parse the message
@ -141,115 +202,17 @@ class ParseThread(threading.Thread):
nick, user, host = irc_netmask_rem(prefix).groups()
mask = nick + "!" + user + "@" + host
paramlist = irc_param_ref(params)
lastparam = ""
lastparam = ""
if paramlist:
if paramlist[-1].startswith(':'):
paramlist[-1] = paramlist[-1][1:]
lastparam = paramlist[-1]
# put the parsed message in the response queue
self.parsed_queue.put([msg, prefix, command, params, nick, user, host,
mask, paramlist, lastparam])
self.out.put([msg, prefix, command, params, nick, user, host,
mask, paramlist, lastparam])
# if the server pings us, pong them back
if command == "PING":
string = "PONG :" + paramlist[0]
self.output_queue.put(string)
class IRCConnection(object):
"""handles an IRC connection"""
def __init__(self, name, host, port, input_queue, output_queue):
self.output_queue = output_queue # lines to be sent out
self.input_queue = input_queue # lines that were received
self.socket = self.create_socket()
self.conn_name = name
self.host = host
self.port = port
self.timeout = 300
def create_socket(self):
return socket.socket(socket.AF_INET, socket.TCP_NODELAY)
def connect(self):
self.socket.connect((self.host, self.port))
self.receive_thread = ReceiveThread(self.socket, self.input_queue, self.timeout)
self.receive_thread.start()
self.send_thread = SendThread(self.socket, self.conn_name, self.output_queue)
self.send_thread.start()
def stop(self):
self.send_thread.shutdown = True
self.receive_thread.shutdown = True
time.sleep(0.1)
self.socket.close()
def reconnect(self):
self.stop()
self.connect()
class SSLIRCConnection(IRCConnection):
"""handles a SSL IRC connection"""
def __init__(self, name, host, port, input_queue, output_queue, ignore_cert_errors):
self.ignore_cert_errors = ignore_cert_errors
IRCConnection.__init__(self, name, host, port, input_queue, output_queue)
def create_socket(self):
return wrap_socket(IRCConnection.create_socket(self), server_side=False,
cert_reqs=CERT_NONE if self.ignore_cert_errors else
CERT_REQUIRED)
class BotInstance(object):
""" A BotInstance represents each connection the bot makes to an IRC server """
def __init__(self, name, server, nick, port=6667, ssl=False, logger=None, channels=[], config={}):
self.name = name
self.channels = channels
self.config = config
self.ssl = ssl
self.server = server
self.port = port
self.logger = logger
self.nick = nick
self.vars = {}
self.history = {}
self.parsed_queue = queue.Queue() # responses from the server are placed here
# format: [rawline, prefix, command, params,
# nick, user, host, paramlist, msg]
self.parsed_queue = queue.Queue()
self.input_queue = queue.Queue()
self.output_queue = queue.Queue()
# create the IRC connection and connect
self.connection = self.create_connection()
self.connection.connect()
self.set_pass(self.config.get('server_password'))
self.set_nick(self.nick)
self.cmd("USER",
[self.config.get('user', 'cloudbot'), "3", "*",
self.config.get('realname', 'CloudBot - http://git.io/cloudbot')])
self.parse_thread = ParseThread(self.input_queue, self.output_queue,
self.parsed_queue)
self.parse_thread.daemon = True
self.parse_thread.start()
def create_connection(self):
if self.ssl:
return SSLIRCConnection(self.name, self.server, self.port, self.input_queue,
self.output_queue, True)
else:
return IRCConnection(self.name, self.server, self.port,
self.input_queue, self.output_queue)
def stop(self):
self.connection.stop()
self.cmd("PONG", paramlist)
def set_pass(self, password):
if password:
@ -276,20 +239,25 @@ class BotInstance(object):
def ctcp(self, target, ctcp_type, text):
""" makes the bot send a PRIVMSG CTCP to a target """
out = "\x01{} {}\x01".format(ctcp_type, text)
out = u"\x01{} {}\x01".format(ctcp_type, text)
self.cmd("PRIVMSG", [target, out])
def cmd(self, command, params=None):
if params:
params[-1] = ':' + params[-1]
self.send("{} {}".format(command, ' '.join(params)))
params[-1] = u':' + params[-1]
self.send(u"{} {}".format(command, ' '.join(params)))
else:
self.send(command)
def send(self, string):
try:
self.logger.info("{} >> {}".format(self.name.upper(), string))
except:
# if this doesn't work, no big deal
pass
self.output_queue.put(string)
def send(self, str):
self.conn.oqueue.put(str)
class SSLIRC(IRC):
def __init__(self, name, server, nick, port=6667, channels=[], conf={},
ignore_certificate_errors=True):
self.ignore_cert_errors = ignore_certificate_errors
IRC.__init__(self, name, server, nick, port, channels, conf)
def create_connection(self):
return crlf_ssl_tcp(self.server, self.port, self.ignore_cert_errors)

View file

@ -1,153 +0,0 @@
import os
import re
import glob
import collections
from watchdog.observers import Observer
from watchdog.tricks import Trick
from pprint import pprint
from core import main
def make_signature(f):
return f.__code__.co_filename, f.__name__, f.__code__.co_firstlineno
def format_plug(plug, kind='', lpad=0):
out = ' ' * lpad + '{}:{}:{}'.format(*make_signature(plug[0]))
if kind == 'command':
out += ' ' * (50 - len(out)) + plug[1]['name']
if kind == 'event':
out += ' ' * (50 - len(out)) + ', '.join(plug[1]['events'])
if kind == 'regex':
out += ' ' * (50 - len(out)) + plug[1]['regex']
return out
class PluginLoader(object):
def __init__(self, bot):
self.observer = Observer()
self.path = os.path.abspath("plugins")
self.bot = bot
self.event_handler = PluginEventHandler(self, patterns=["*.py"])
self.observer.schedule(self.event_handler, self.path, recursive=False)
self.observer.start()
self.load_all()
def stop(self):
"""shuts down the plugin reloader"""
self.observer.stop()
def load_all(self):
"""runs load_file() on all python files in the plugins folder"""
files = set(glob.glob(os.path.join(self.path, '*.py')))
for f in files:
self.load_file(f, rebuild=True)
self.rebuild()
def load_file(self, path, rebuild=False):
"""loads (or reloads) all valid plugins from a specified file"""
filename = os.path.basename(path)
title = os.path.splitext(filename)[0]
disabled = self.bot.config.get('disabled_plugins', [])
if title in disabled:
self.bot.logger.info("Did not load plugins from: {} (plugin disabled)".format(filename))
return
# compile the file and eval it in a namespace
try:
code = compile(open(path, 'U').read(), filename, 'exec')
namespace = {}
eval(code, namespace)
except Exception:
self.bot.logger.exception("Error compiling {}:".format(filename))
return
# remove plugins already loaded from this file
for plug_type, data in self.bot.plugins.items():
self.bot.plugins[plug_type] = [x for x in data
if x[0]._filename != filename]
# stop all currently running instances of the plugins from this file
for func, handler in list(self.bot.threads.items()):
if func._filename == filename:
handler.stop()
del self.bot.threads[func]
# find objects with hooks in the plugin namespace
# TODO: kill it with fire, kill it all
for obj in namespace.values():
if hasattr(obj, '_hook'): # check for magic
if obj._thread:
self.bot.threads[obj] = main.Handler(self.bot, obj)
for plug_type, data in obj._hook:
# add plugin to the plugin list
self.bot.plugins[plug_type] += [data]
self.bot.logger.info("Loaded plugin: {} ({})".format(format_plug(data), plug_type))
# do a rebuild, unless the bot is loading all plugins (rebuild happens after load_all)
if not rebuild:
self.rebuild()
def unload_file(self, path):
"""unloads all loaded plugins from a specified file"""
filename = os.path.basename(path)
self.bot.logger.info("Unloading plugins from: {}".format(filename))
# remove plugins loaded from this file
for plugin_type, plugins in self.bot.plugins.items():
self.bot.plugins[plugin_type] = [x for x in plugins if x[0]._filename != filename]
# stop all currently running instances of the plugins from this file
for func, handler in list(self.bot.threads.items()):
if func._filename == filename:
handler.stop()
del self.bot.threads[func]
self.rebuild()
def rebuild(self):
"""rebuilds the cloudbot command and event hook lists"""
self.bot.commands = {}
for plugin in self.bot.plugins['command']:
name = plugin[1]['name'].lower()
if not re.match(r'^\w+$', name):
self.bot.logger.error('Invalid command name: "{}" ({})'.format(name, format_plug(plugin)))
continue
if name in self.bot.commands:
self.bot.logger.error('Command already registered: "{}" ({}, {})'.format(name,
format_plug(self.bot.commands[name]),
format_plug(plugin)))
continue
self.bot.commands[name] = plugin
self.bot.events = collections.defaultdict(list)
for func, args in self.bot.plugins['event']:
for event in args['events']:
self.bot.events[event].append((func, args))
class PluginEventHandler(Trick):
def __init__(self, loader, *args, **kwargs):
self.loader = loader
Trick.__init__(self, *args, **kwargs)
def on_created(self, event):
self.loader.load_file(event.src_path)
def on_deleted(self, event):
self.loader.unload_file(event.src_path)
def on_modified(self, event):
self.loader.load_file(event.src_path)
def on_moved(self, event):
self.loader.unload_file(event.src_path)
self.loader.load_file(event.dest_path)

View file

@ -1,16 +1,12 @@
import _thread
import thread
import traceback
import queue
import re
from sqlalchemy.orm import scoped_session
_thread.stack_size(1024 * 512) # reduce vm size
#TODO: redesign this messy thing
thread.stack_size(1024 * 512) # reduce vm size
class Input(dict):
def __init__(self, bot, conn, raw, prefix, command, params,
def __init__(self, conn, raw, prefix, command, params,
nick, user, host, mask, paraml, msg):
chan = paraml[0].lower()
@ -26,7 +22,7 @@ class Input(dict):
if target == nick:
conn.msg(target, message)
else:
conn.msg(target, "({}) {}".format(nick, message))
conn.msg(target, u"({}) {}".format(nick, message))
def action(message, target=chan):
"""sends an action to the current channel/user or a specific channel/user"""
@ -54,59 +50,67 @@ class Input(dict):
self[key] = value
def run(bot, func, input):
uses_db = True
# TODO: change to bot.get_db_session()
print(input)
if 'text' not in input:
input.text = input.paraml
def run(func, input):
args = func._args
if uses_db:
# create SQLAlchemy session
bot.logger.debug("Opened DB session for: {}".format(func._filename))
input.db = input.bot.db_session()
try:
out = func(input, input.conn)
except:
bot.logger.exception("Error in plugin {}:".format(func._filename))
return
finally:
if uses_db:
bot.logger.debug("Closed DB session for: {}".format(func._filename))
input.db.close()
if 'inp' not in input:
input.inp = input.paraml
if args:
if 'db' in args and 'db' not in input:
input.db = get_db_connection(input.conn)
if 'input' in args:
input.input = input
if 0 in args:
out = func(input.inp, **input)
else:
kw = dict((key, input[key]) for key in args if key in input)
out = func(input.inp, **kw)
else:
out = func(input.inp)
if out is not None:
input.reply(str(out))
input.reply(unicode(out))
def do_sieve(sieve, bot, input, func, type, args):
try:
return sieve(bot, input, func, type, args)
except Exception:
bot.logger.exception("Error in sieve {}:".format(func._filename))
print 'sieve error',
traceback.print_exc()
return None
class Handler(object):
"""Runs plugins in their own threads (ensures order)"""
def __init__(self, bot, func):
def __init__(self, func):
self.func = func
self.bot = bot
self.input_queue = queue.Queue()
_thread.start_new_thread(self.start, ())
self.input_queue = Queue.Queue()
thread.start_new_thread(self.start, ())
def start(self):
uses_db = True
uses_db = 'db' in self.func._args
db_conns = {}
while True:
input = self.input_queue.get()
if input == StopIteration:
break
run(self.bot, self.func, input)
if uses_db:
db = db_conns.get(input.conn)
if db is None:
db = bot.get_db_connection(input.conn)
db_conns[input.conn] = db
input.db = db
try:
run(self.func, input)
except:
import traceback
traceback.print_exc()
def stop(self):
self.input_queue.put(StopIteration)
@ -115,27 +119,27 @@ class Handler(object):
self.input_queue.put(value)
def dispatch(bot, input, kind, func, args, autohelp=False):
for sieve, in bot.plugins['sieve']:
def dispatch(input, kind, func, args, autohelp=False):
for sieve, in bot.plugs['sieve']:
input = do_sieve(sieve, bot, input, func, kind, args)
if input is None:
return
if not (not autohelp or not args.get('autohelp', True) or input.inp or not (func.__doc__ is not None)):
input.notice(input.conn.config["command_prefix"] + func.__doc__)
input.notice(input.conn.conf["command_prefix"] + func.__doc__)
return
if func._thread:
bot.threads[func].put(input)
else:
_thread.start_new_thread(run, (bot, func, input))
thread.start_new_thread(run, (func, input))
def match_command(bot, command):
def match_command(command):
commands = list(bot.commands)
# do some fuzzy matching
prefix = [x for x in commands if x.startswith(command)]
prefix = filter(lambda x: x.startswith(command), commands)
if len(prefix) == 1:
return prefix[0]
elif prefix and command not in prefix:
@ -144,13 +148,13 @@ def match_command(bot, command):
return command
def main(bot, conn, out):
inp = Input(bot, conn, *out)
command_prefix = conn.config.get('command_prefix', '.')
def main(conn, out):
inp = Input(conn, *out)
command_prefix = conn.conf.get('command_prefix', '.')
# EVENTS
for func, args in bot.events[inp.command] + bot.events['*']:
dispatch(bot, Input(bot, conn, *out), "event", func, args)
dispatch(Input(conn, *out), "event", func, args)
if inp.command == 'PRIVMSG':
# COMMANDS
@ -158,6 +162,7 @@ def main(bot, conn, out):
prefix = '^(?:[{}]?|'.format(command_prefix)
else:
prefix = '^(?:[{}]|'.format(command_prefix)
command_re = prefix + inp.conn.nick
command_re += r'[,;:]+\s+)(\w+)(?:$|\s+)(.*)'
@ -165,26 +170,26 @@ def main(bot, conn, out):
if m:
trigger = m.group(1).lower()
command = match_command(bot, trigger)
command = match_command(trigger)
if isinstance(command, list): # multiple potential matches
input = Input(bot, conn, *out)
input = Input(conn, *out)
input.notice("Did you mean {} or {}?".format
(', '.join(command[:-1]), command[-1]))
elif command in bot.commands:
input = Input(bot, conn, *out)
input = Input(conn, *out)
input.trigger = trigger
input.text_unstripped = m.group(2)
input.text = input.text_unstripped.strip()
input.inp_unstripped = m.group(2)
input.inp = input.inp_unstripped.strip()
func, args = bot.commands[command]
dispatch(bot, input, "command", func, args, autohelp=True)
dispatch(input, "command", func, args, autohelp=True)
# REGEXES
for func, args in bot.plugins['regex']:
for func, args in bot.plugs['regex']:
m = args['re'].search(inp.lastparam)
if m:
input = Input(bot, conn, *out)
input.text = m
input = Input(conn, *out)
input.inp = m
dispatch(bot, input, "regex", func, args)
dispatch(input, "regex", func, args)

View file

@ -1,48 +0,0 @@
from fnmatch import fnmatch
class PermissionManager(object):
def __init__(self, bot, conn):
# this is all legacy code, needs to be redone with classes and whatnot
self.logger = bot.logger
self.logger.info("Creating simple permission manager for {}.".format(conn.name))
# stuff
self.bot = bot
self.conn = conn
self.config = conn.config
self.group_perms = {}
self.group_users = {}
self.perm_users = {}
self.reload()
def reload(self):
self.logger.info("Reloading permissions for {}.".format(self.conn.name))
groups = self.conn.config.get("permissions", [])
# work out the permissions and users each group has
for key, value in groups.items():
self.group_perms[key] = []
self.group_users[key] = []
for permission in value["perms"]:
self.group_perms[key].append(permission)
for user in value["users"]:
self.group_users[key].append(user)
for group, users in self.group_users.items():
group_perms = self.group_perms[group]
for perm in group_perms:
self.perm_users[perm] = []
self.perm_users[perm] = users
def has_perm_mask(self, mask, perm):
allowed_users = self.perm_users[perm]
for pattern in allowed_users:
if fnmatch(mask.lower(), pattern.lower()):
return input

160
core/reload.py Normal file
View file

@ -0,0 +1,160 @@
import collections
import glob
import os
import re
import sys
import traceback
if 'mtimes' not in globals():
mtimes = {}
if 'lastfiles' not in globals():
lastfiles = set()
def make_signature(f):
return f.func_code.co_filename, f.func_name, f.func_code.co_firstlineno
def format_plug(plug, kind='', lpad=0):
out = ' ' * lpad + '{}:{}:{}'.format(*make_signature(plug[0]))
if kind == 'command':
out += ' ' * (50 - len(out)) + plug[1]['name']
if kind == 'event':
out += ' ' * (50 - len(out)) + ', '.join(plug[1]['events'])
if kind == 'regex':
out += ' ' * (50 - len(out)) + plug[1]['regex']
return out
def reload(init=False):
changed = False
if init:
bot.plugs = collections.defaultdict(list)
bot.threads = {}
core_fileset = set(glob.glob(os.path.join("core", "*.py")))
for filename in core_fileset:
mtime = os.stat(filename).st_mtime
if mtime != mtimes.get(filename):
mtimes[filename] = mtime
changed = True
try:
eval(compile(open(filename, 'U').read(), filename, 'exec'),
globals())
except Exception:
traceback.print_exc()
if init: # stop if there's an error (syntax?) in a core
sys.exit() # script on startup
continue
if filename == os.path.join('core', 'reload.py'):
reload(init=init)
return
fileset = set(glob.glob(os.path.join('plugins', '*.py')))
# remove deleted/moved plugins
for name, data in bot.plugs.iteritems():
bot.plugs[name] = [x for x in data if x[0]._filename in fileset]
for filename in list(mtimes):
if filename not in fileset and filename not in core_fileset:
mtimes.pop(filename)
for func, handler in list(bot.threads.iteritems()):
if func._filename not in fileset:
handler.stop()
del bot.threads[func]
# compile new plugins
for filename in fileset:
mtime = os.stat(filename).st_mtime
if mtime != mtimes.get(filename):
mtimes[filename] = mtime
changed = True
try:
code = compile(open(filename, 'U').read(), filename, 'exec')
namespace = {}
eval(code, namespace)
except Exception:
traceback.print_exc()
continue
# remove plugins already loaded from this filename
for name, data in bot.plugs.iteritems():
bot.plugs[name] = [x for x in data
if x[0]._filename != filename]
for func, handler in list(bot.threads.iteritems()):
if func._filename == filename:
handler.stop()
del bot.threads[func]
for obj in namespace.itervalues():
if hasattr(obj, '_hook'): # check for magic
if obj._thread:
bot.threads[obj] = Handler(obj)
for type, data in obj._hook:
bot.plugs[type] += [data]
if not init:
print '### new plugin (type: %s) loaded:' % \
type, format_plug(data)
if changed:
bot.commands = {}
for plug in bot.plugs['command']:
name = plug[1]['name'].lower()
if not re.match(r'^\w+$', name):
print '### ERROR: invalid command name "{}" ({})'.format(name, format_plug(plug))
continue
if name in bot.commands:
print "### ERROR: command '{}' already registered ({}, {})".format(name,
format_plug(bot.commands[name]),
format_plug(plug))
continue
bot.commands[name] = plug
bot.events = collections.defaultdict(list)
for func, args in bot.plugs['event']:
for event in args['events']:
bot.events[event].append((func, args))
if init:
print ' plugin listing:'
if bot.commands:
# hack to make commands with multiple aliases
# print nicely
print ' command:'
commands = collections.defaultdict(list)
for name, (func, args) in bot.commands.iteritems():
commands[make_signature(func)].append(name)
for sig, names in sorted(commands.iteritems()):
names.sort(key=lambda x: (-len(x), x)) # long names first
out = ' ' * 6 + '%s:%s:%s' % sig
out += ' ' * (50 - len(out)) + ', '.join(names)
print out
for kind, plugs in sorted(bot.plugs.iteritems()):
if kind == 'command':
continue
print ' {}:'.format(kind)
for plug in plugs:
print format_plug(plug, kind=kind, lpad=6)
print

View file

@ -3,15 +3,15 @@ import random
from util import hook
with open("data/larts.txt") as f:
with open("plugins/data/larts.txt") as f:
larts = [line.strip() for line in f.readlines()
if not line.startswith("//")]
with open("data/insults.txt") as f:
with open("plugins/data/insults.txt") as f:
insults = [line.strip() for line in f.readlines()
if not line.startswith("//")]
with open("data/flirts.txt") as f:
with open("plugins/data/flirts.txt") as f:
flirts = [line.strip() for line in f.readlines()
if not line.startswith("//")]

121
disabled_stuff/cleverbot.py Normal file
View file

@ -0,0 +1,121 @@
# from jessi bot
import urllib2
import hashlib
import re
import unicodedata
from util import hook
# these are just parts required
# TODO: Merge them.
arglist = ['', 'y', '', '', '', '', '', '', '', '', 'wsf', '',
'', '', '', '', '', '', '', '0', 'Say', '1', 'false']
always_safe = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
'abcdefghijklmnopqrstuvwxyz'
'0123456789' '_.-')
headers = {'X-Moz': 'prefetch', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:7.0.1)Gecko/20100101 Firefox/7.0',
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 'Referer': 'http://www.cleverbot.com',
'Pragma': 'no-cache', 'Cache-Control': 'no-cache, no-cache', 'Accept-Language': 'en-us;q=0.8,en;q=0.5'}
keylist = ['stimulus', 'start', 'sessionid', 'vText8', 'vText7', 'vText6',
'vText5', 'vText4', 'vText3', 'vText2', 'icognoid',
'icognocheck', 'prevref', 'emotionaloutput', 'emotionalhistory',
'asbotname', 'ttsvoice', 'typing', 'lineref', 'fno', 'sub',
'islearning', 'cleanslate']
MsgList = list()
def quote(s, safe='/'): # quote('abc def') -> 'abc%20def'
s = s.encode('utf-8')
s = s.decode('utf-8')
print "s= " + s
print "safe= " + safe
safe += always_safe
safe_map = dict()
for i in range(256):
c = chr(i)
safe_map[c] = (c in safe) and c or ('%%%02X' % i)
try:
res = map(safe_map.__getitem__, s)
except:
print "blank"
return ''
print "res= " + ''.join(res)
return ''.join(res)
def encode(keylist, arglist):
text = str()
for i in range(len(keylist)):
k = keylist[i]
v = quote(arglist[i])
text += '&' + k + '=' + v
text = text[1:]
return text
def Send():
data = encode(keylist, arglist)
digest_txt = data[9:29]
new_hash = hashlib.md5(digest_txt).hexdigest()
arglist[keylist.index('icognocheck')] = new_hash
data = encode(keylist, arglist)
req = urllib2.Request('http://www.cleverbot.com/webservicemin',
data, headers)
f = urllib2.urlopen(req)
reply = f.read()
return reply
def parseAnswers(text):
d = dict()
keys = ['text', 'sessionid', 'logurl', 'vText8', 'vText7', 'vText6',
'vText5', 'vText4', 'vText3', 'vText2', 'prevref', 'foo',
'emotionalhistory', 'ttsLocMP3', 'ttsLocTXT', 'ttsLocTXT3',
'ttsText', 'lineRef', 'lineURL', 'linePOST', 'lineChoices',
'lineChoicesAbbrev', 'typingData', 'divert']
values = text.split('\r')
i = 0
for key in keys:
d[key] = values[i]
i += 1
return d
def ask(inp):
arglist[keylist.index('stimulus')] = inp
if MsgList:
arglist[keylist.index('lineref')] = '!0' + str(len(
MsgList) / 2)
asw = Send()
MsgList.append(inp)
answer = parseAnswers(asw)
for k, v in answer.iteritems():
try:
arglist[keylist.index(k)] = v
except ValueError:
pass
arglist[keylist.index('emotionaloutput')] = str()
text = answer['ttsText']
MsgList.append(text)
return text
@hook.command("cb")
def cleverbot(inp, reply=None):
reply(ask(inp))
''' # TODO: add in command to control extra verbose per channel
@hook.event('PRIVMSG')
def cbevent(inp, reply=None):
reply(ask(inp))
@hook.command("cbver", permissions=['cleverbot'])
def cleverbotverbose(inp, notice=None):
if on in input
'''

126
disabled_stuff/cloudbot.sh Normal file
View file

@ -0,0 +1,126 @@
#!/bin/bash
echo ""
echo " ________ ______ __ "
echo " / ____/ /___ __ ______/ / __ )____ / /_"
echo " / / / / __ \/ / / / __ / __ / __ \/ __/"
echo "/ /___/ / /_/ / /_/ / /_/ / /_/ / /_/ / /_ "
echo "\____/_/\____/\__,_/\__,_/_____/\____/\__/ "
echo " http://git.io/cloudbotirc by ClouDev "
echo ""
locatefiles() {
botfile="/bot.py"
botfile=$(pwd)$botfile
logfile="/bot.log"
logfile=$(pwd)$logfile
}
running() {
if [[ $(ps aux|grep bot.py|grep -v grep|grep -v daemon|grep -v SCREEN) != "" ]]; then
true
else
false
fi
}
checkbackend() {
if dpkg -l| grep ^ii|grep daemon|grep 'turns other' > /dev/null; then
backend="daemon"
elif dpkg -l| grep ^ii|grep screen|grep 'terminal multi' > /dev/null; then
backend="screen"
else
backend="manual"
fi
return 0
}
setcommands() {
status() {
if running; then
echo "CloudBot is running!"
else
echo "CloudBot is not running!"
fi
}
clear() {
: > $logfile
}
if [ "$backend" == "daemon" ]; then
start() {
daemon -r -n cloudbot -O $logfile python $botfile
}
stop() {
daemon -n cloudbot --stop
}
elif [ "$backend" == "screen" ]; then
start() {
screen -d -m -S cloudbot -t cloudbot python $botfile > $logfile 2>&1
}
stop() {
pid=`ps ax|grep -v grep|grep python|grep -v SCREEN|grep $botfile|awk '{print $1}'`
kill $pid
}
elif [ "$backend" == "manual" ]; then
start() {
$botfile
}
stop() {
pid=`ps ax|grep -v grep|grep python|grep $botfile|awk '{print $1}'`
kill $pid
}
fi
}
processargs() {
case $1 in
start|-start|--start)
if running; then
echo "Cannot start! Bot is already running!"
exit 1
else
echo "Starting CloudBot... ($backend)"
start
fi
;;
stop|-stop|--stop)
if running; then
echo "Stopping CloudBot... ($backend)"
stop
else
echo "Cannot stop! Bot is not already running!"
exit 1
fi
;;
restart|-restart|--restart)
if running; then
echo "Restarting CloudBot... ($backend)"
stop
sleep 3
start
else
echo "Cannot restart! Bot is not already running!"
exit 1
fi
;;
clear|-clear|--clear)
echo "Clearing logs..."
clear
;;
status|-status|--status)
status
;;
*)
usage="usage: ./cloudbot {start|stop|restart|clear|status}"
echo $usage
;;
esac
}
main() {
locatefiles
checkbackend
setcommands
processargs $1
}
main $*
exit 0

View file

@ -0,0 +1,37 @@
from util import hook
import re
CORRECTION_RE = r'^(s|S)/.*/.*/?\S*$'
@hook.regex(CORRECTION_RE)
def correction(match, input=None, conn=None, message=None):
split = input.msg.split("/")
if len(split) == 4:
nick = split[3].lower()
else:
nick = None
find = split[1]
replace = split[2]
for item in conn.history[input.chan].__reversed__():
name, timestamp, msg = item
if msg.startswith("s/"):
# don't correct corrections, it gets really confusing
continue
if nick:
if nick != name.lower():
continue
if find in msg:
if "\x01ACTION" in msg:
msg = msg.replace("\x01ACTION ", "/me ").replace("\x01", "")
message(u"Correction, <{}> {}".format(name, msg.replace(find, "\x02" + replace + "\x02")))
return
else:
continue
return u"Did not find {} in any recent messages.".format(find)

View file

@ -5,21 +5,21 @@ from util import http, hook
exchanges = {
"blockchain": {
"api_url": "https://blockchain.info/ticker",
"func": lambda data: "Blockchain // Buy: \x0307${:,.2f}\x0f -"
" Sell: \x0307${:,.2f}\x0f".format(data["USD"]["buy"], data["USD"]["sell"])
"func": lambda data: u"Blockchain // Buy: \x0307${:,.2f}\x0f -"
u" Sell: \x0307${:,.2f}\x0f".format(data["USD"]["buy"], data["USD"]["sell"])
},
"coinbase": {
"api_url": "https://coinbase.com/api/v1/prices/spot_rate",
"func": lambda data: "Coinbase // Current: \x0307${:,.2f}\x0f".format(float(data['amount']))
"func": lambda data: u"Coinbase // Current: \x0307${:,.2f}\x0f".format(float(data['amount']))
},
"bitpay": {
"api_url": "https://bitpay.com/api/rates",
"func": lambda data: "Bitpay // Current: \x0307${:,.2f}\x0f".format(data[0]['rate'])
"func": lambda data: u"Bitpay // Current: \x0307${:,.2f}\x0f".format(data[0]['rate'])
},
"bitstamp": {
"api_url": "https://www.bitstamp.net/api/ticker/",
"func": lambda data: "BitStamp // Current: \x0307${:,.2f}\x0f - High: \x0307${:,.2f}\x0f -"
" Low: \x0307${:,.2f}\x0f - Volume: {:,.2f} BTC".format(float(data['last']),
"func": lambda data: u"BitStamp // Current: \x0307${:,.2f}\x0f - High: \x0307${:,.2f}\x0f -"
u" Low: \x0307${:,.2f}\x0f - Volume: {:,.2f} BTC".format(float(data['last']),
float(data['high']),
float(data['low']),
float(data['volume']))

Binary file not shown.

View file

@ -1,6 +1,5 @@
# Written by Scaevolus, updated by Lukeroge
import re
import random
@ -18,15 +17,15 @@ split_re = re.compile(r'([\d+-]*)d?(F|\d*)', re.I)
def n_rolls(count, n):
"""roll an n-sided die count times"""
if n == "F":
return [random.randint(-1, 1) for x in range(min(count, 100))]
return [random.randint(-1, 1) for x in xrange(min(count, 100))]
if n < 2: # it's a coin
if count < 100:
return [random.randint(0, 1) for x in range(count)]
return [random.randint(0, 1) for x in xrange(count)]
else: # fake it
return [int(random.normalvariate(.5 * count, (.75 * count) ** .5))]
else:
if count < 100:
return [random.randint(1, n) for x in range(count)]
return [random.randint(1, n) for x in xrange(count)]
else: # fake it
return [int(random.normalvariate(.5 * (1 + n) * count,
(((n + 1) * (2 * n + 1) / 6. -
@ -75,7 +74,7 @@ def dice(inp):
try:
if count > 0:
d = n_rolls(count, side)
rolls += list(map(str, d))
rolls += map(str, d)
total += sum(d)
else:
d = n_rolls(-count, side)

View file

@ -19,10 +19,10 @@ def define(inp):
'//div[@class="example"]')
if not definition:
return 'No results for ' + inp + ' :('
return u'No results for {} :('.format(inp)
def format_output(show_examples):
result = '{}: '.format(h.xpath('//dt[@class="title-word"]/a/text()')[0])
result = u'{}: '.format(h.xpath('//dt[@class="title-word"]/a/text()')[0])
correction = h.xpath('//span[@class="correct-word"]/text()')
if correction:
@ -41,7 +41,7 @@ def define(inp):
for article in sections:
result += article[0]
if len(article) > 2:
result += ' '.join('{}. {}'.format(n + 1, section)
result += u' '.join(u'{}. {}'.format(n + 1, section)
for n, section in enumerate(article[1:]))
else:
result += article[1] + ' '
@ -77,7 +77,7 @@ def etymology(inp):
etym = h.xpath('//dl')
if not etym:
return 'No etymology found for {} :('.format(inp)
return u'No etymology found for {} :('.format(inp)
etym = etym[0].text_content()

View file

@ -1,4 +1,4 @@
import urllib.parse
import urlparse
from util import hook, http
@ -10,7 +10,7 @@ def down(inp):
if 'http://' not in inp:
inp = 'http://' + inp
inp = 'http://' + urllib.parse.urlparse(inp).netloc
inp = 'http://' + urlparse.urlparse(inp).netloc
# http://mail.python.org/pipermail/python-list/2006-December/589854.html
try:

View file

@ -9,15 +9,15 @@ color_codes = {
"<y>": "\x02"
}
with open("./data/8ball_responses.txt") as f:
with open("plugins/data/8ball_responses.txt") as f:
responses = [line.strip() for line in
f.readlines() if not line.startswith("//")]
@hook.command()
def eightball(input, conn):
@hook.command('8ball')
def eightball(inp, action=None):
"""8ball <question> -- The all knowing magic eight ball,
in electronic form. Ask and it shall be answered!"""
magic = text.multiword_replace(random.choice(responses), color_codes)
input.action("shakes the magic 8 ball... {}".format(magic))
action("shakes the magic 8 ball... {}".format(magic))

View file

@ -37,8 +37,8 @@ def get_salt(bot):
"""generate an encryption salt if none exists, then returns the salt"""
if not bot.config.get("random_salt", False):
bot.config["random_salt"] = hashlib.md5(os.urandom(16)).hexdigest()
bot.config.save_config()
return bot.config.get("random_salt")
json.dump(bot.config, open('config', 'w'), sort_keys=True, indent=2)
return bot.config["random_salt"]
@hook.command
@ -69,8 +69,7 @@ def encrypt(inp, bot=None, db=None, notice=None):
# store the encoded text and IV in the DB for decoding later
db.execute("insert or replace into encryption(encrypted, iv)"
"values(:encoded,:iv)", {'encoded': encoded,
'iv': iv_encoded})
"values(?,?)", (encoded, iv_encoded))
db.commit()
return encoded
@ -98,7 +97,7 @@ def decrypt(inp, bot=None, db=None, notice=None):
# get the encoded IV from the database and decode it
iv_encoded = db.execute("select iv from encryption where"
" encrypted=:text", {'text': text}).fetchone()[0]
" encrypted=?", (text,)).fetchone()[0]
iv = base64.b64decode(iv_encoded)
# create AES cipher, decode text, decrypt text, and unpad it

View file

@ -114,6 +114,10 @@ def info(inp, notice=None, db=None):
@hook.regex(r'^\? ?(.+)')
def factoid(inp, message=None, db=None, bot=None, action=None, conn=None, input=None):
"""?<word> -- Shows what data is associated with <word>."""
try:
prefix_on = bot.config["plugins"]["factoids"].get("prefix", False)
except KeyError:
prefix_on = False
db_init(db)
@ -152,4 +156,7 @@ def factoid(inp, message=None, db=None, bot=None, action=None, conn=None, input=
except http.HttpError:
message("Could not fetch URL.")
else:
message(result)
if prefix_on:
message("\x02[{}]:\x02 {}".format(factoid_id, result))
else:
message(result)

View file

@ -1,4 +1,4 @@
from urllib.parse import quote_plus
from urllib import quote_plus
from util import hook, http
@ -44,7 +44,7 @@ def bancount(inp):
services = request["stats"]["service"]
out = []
for service, ban_count in list(services.items()):
for service, ban_count in services.items():
if ban_count != 0:
out.append("{}: \x02{}\x02".format(service, ban_count))
else:

View file

@ -3,8 +3,7 @@ import random
from util import hook
with open("./data/fortunes.txt") as f:
with open("plugins/data/fortunes.txt") as f:
fortunes = [line.strip() for line in f.readlines()
if not line.startswith("//")]

13
disabled_stuff/freddy.py Normal file
View file

@ -0,0 +1,13 @@
from util import hook, http, web
from subprocess import check_output, CalledProcessError
@hook.command
def freddycode(inp):
"""freddycode <code> - Check if the Freddy Fresh code is correct."""
try:
return "Freddy: '%s' ist %s" % (inp, \
check_output(["/bin/freddycheck", inp]))
except CalledProcessError as err:
return "Freddy: Skript returned %s" % (str(err))

View file

@ -1,7 +1,7 @@
import os.path
import json
import gzip
from io import StringIO
from StringIO import StringIO
import pygeoip
@ -9,22 +9,22 @@ from util import hook, http
# load region database
with open("./data/geoip_regions.json", "rb") as f:
with open("./plugins/data/geoip_regions.json", "rb") as f:
regions = json.loads(f.read())
if os.path.isfile(os.path.abspath("./data/GeoLiteCity.dat")):
if os.path.isfile(os.path.abspath("./plugins/data/GeoLiteCity.dat")):
# initialise geolocation database
geo = pygeoip.GeoIP(os.path.abspath("./data/GeoLiteCity.dat"))
geo = pygeoip.GeoIP(os.path.abspath("./plugins/data/GeoLiteCity.dat"))
else:
download = http.get("http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz")
string_io = StringIO(download)
geoip_file = gzip.GzipFile(fileobj=string_io, mode='rb')
output = open(os.path.abspath("./data/GeoLiteCity.dat"), 'wb')
output = open(os.path.abspath("./plugins/data/GeoLiteCity.dat"), 'wb')
output.write(geoip_file.read())
output.close()
geo = pygeoip.GeoIP(os.path.abspath("./data/GeoLiteCity.dat"))
geo = pygeoip.GeoIP(os.path.abspath("./plugins/data/GeoLiteCity.dat"))
@hook.command
@ -51,4 +51,4 @@ def geoip(inp):
data["cc"] = record["country_code"] or "N/A"
data["country"] = record["country_name"] or "Unknown"
data["city"] = record["city"] or "Unknown"
return "\x02Country:\x02 {country} ({cc}), \x02City:\x02 {city}{region}".format(**data)
return u"\x02Country:\x02 {country} ({cc}), \x02City:\x02 {city}{region}".format(**data)

View file

@ -1,5 +1,5 @@
import json
import urllib.request, urllib.error, urllib.parse
import urllib2
from util import hook, http
@ -37,18 +37,18 @@ def ghissues(inp):
except IndexError:
return "Invalid syntax. .github issues username/repo [number]"
try:
url += "/{}".format(args[1])
url += "/%s" % args[1]
number = True
except IndexError:
number = False
try:
data = json.loads(http.open(url).read())
print(url)
print url
if not number:
try:
data = data[0]
except IndexError:
print(data)
print data
return "Repo has no open issues"
except ValueError:
return "Invalid data returned. Check arguments (.github issues username/repo [number]"
@ -56,9 +56,9 @@ def ghissues(inp):
fmt1 = "Issue: #%s (%s) by %s: %s %s" # (number, state, user.login, title, gitio.gitio(data.url))
number = data["number"]
if data["state"] == "open":
state = "\x033\x02OPEN\x02\x0f"
state = u"\x033\x02OPEN\x02\x0f"
else:
state = "\x034\x02CLOSED\x02\x0f by {}".format(data["closed_by"]["login"])
state = u"\x034\x02CLOSED\x02\x0f by {}".format(data["closed_by"]["login"])
user = data["user"]["login"]
title = data["title"]
summary = truncate(data["body"])
@ -93,12 +93,12 @@ def gitio(inp):
url = 'url=' + str(url)
if code:
url = url + '&code=' + str(code)
req = urllib.request.Request(url='http://git.io', data=url)
req = urllib2.Request(url='http://git.io', data=url)
# try getting url, catch http error
try:
f = urllib.request.urlopen(req)
except urllib.error.HTTPError:
f = urllib2.urlopen(req)
except urllib2.HTTPError:
return "Failed to get URL!"
urlinfo = str(f.info())
@ -110,7 +110,7 @@ def gitio(inp):
if row.find("Location") != -1:
location = row
print(status)
print status
if not "201" in status:
return "Failed to get URL!"

View file

@ -48,4 +48,4 @@ def google(inp):
content = http.html.fromstring(content).text_content()
content = text.truncate_str(content, 150)
return '{} -- \x02{}\x02: "{}"'.format(result['unescapedUrl'], title, content)
return u'{} -- \x02{}\x02: "{}"'.format(result['unescapedUrl'], title, content)

View file

@ -3,7 +3,7 @@ A Google API key is required and retrieved from the bot config file.
Since December 1, 2011, the Google Translate API is a paid service only.
"""
import html.entities
import htmlentitydefs
import re
from util import hook, http
@ -22,15 +22,15 @@ def unescape(text):
# character reference
try:
if text[:3] == "&#x":
return chr(int(text[3:-1], 16))
return unichr(int(text[3:-1], 16))
else:
return chr(int(text[2:-1]))
return unichr(int(text[2:-1]))
except ValueError:
pass
else:
# named entity
try:
text = chr(html.entities.name2codepoint[text[1:-1]])
text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
except KeyError:
pass
return text # leave as is
@ -83,7 +83,7 @@ def translate(inp, bot=None):
if not api_key:
return "This command requires a paid API key."
args = inp.split(' ', 2)
args = inp.split(u' ', 2)
try:
if len(args) >= 2:
@ -100,7 +100,7 @@ def translate(inp, bot=None):
return goog_trans(api_key, args[1] + ' ' + args[2], sl, 'en')
return goog_trans(api_key, args[2], sl, tl)
return goog_trans(api_key, inp, '', 'en')
except IOError as e:
except IOError, e:
return e

View file

@ -1,5 +1,5 @@
from util import hook
from urllib.parse import unquote
from urllib import unquote
@hook.command(autohelp=False)
def googleurl(inp, db=None, nick=None):

View file

@ -22,11 +22,8 @@ def track_seen(input, message_time, db, conn):
# keep private messages private
if input.chan[:1] == "#" and not re.findall('^s/.*/.*/$', input.msg.lower()):
db.execute("insert or replace into seen_user(name, time, quote, chan, host)"
"values(:name,:time,:quote,:chan,:host)", {'name': input.nick.lower(),
'time': time.time(),
'quote': input.msg,
'chan': input.chan,
'host': input.mask})
"values(?,?,?,?,?)", (input.nick.lower(), message_time, input.msg,
input.chan, input.mask))
db.commit()
@ -59,6 +56,7 @@ def resethistory(inp, input=None, conn=None):
# wat
return "There is no history for this channel."
"""seen.py: written by sklnd in about two beers July 2009"""
@hook.command
def seen(inp, nick='', chan='', db=None, input=None, conn=None):
@ -76,7 +74,7 @@ def seen(inp, nick='', chan='', db=None, input=None, conn=None):
db_init(db, conn.name)
last_seen = db.execute("select name, time, quote from seen_user where name"
" like :name and chan = :chan", {'name': inp, 'chan': chan}).fetchone()
" like ? and chan = ?", (inp, chan)).fetchone()
if last_seen:
reltime = timesince.timesince(last_seen[1])

View file

@ -14,11 +14,6 @@ def db_init(db):
db_ready = True
@hook.onload
def init(paraml, db=None):
db_init(db)
@hook.command(autohelp=False)
def horoscope(inp, db=None, notice=None, nick=None):
"""horoscope <sign> -- Get your horoscope."""
@ -34,8 +29,8 @@ def horoscope(inp, db=None, notice=None, nick=None):
db.execute("create table if not exists horoscope(nick primary key, sign)")
if not sign:
sign = db.execute("select sign from horoscope where "
"nick=lower(:nick)", {'nick': nick}).fetchone()
sign = db.execute("select sign from horoscope where nick=lower(?)",
(nick,)).fetchone()
if not sign:
notice("horoscope <sign> -- Get your horoscope")
return
@ -46,7 +41,7 @@ def horoscope(inp, db=None, notice=None, nick=None):
title = soup.find_all('h1', {'class': 'h1b'})[1]
horoscope_text = soup.find('div', {'class': 'fontdef1'})
result = "\x02{}\x02 {}".format(title, horoscope_text)
result = u"\x02%s\x02 %s" % (title, horoscope_text)
result = text.strip_html(result)
#result = unicode(result, "utf8").replace('flight ','')
@ -54,8 +49,8 @@ def horoscope(inp, db=None, notice=None, nick=None):
return "Could not get the horoscope for {}.".format(inp)
if inp and not dontsave:
db.execute("insert or replace into horoscope(nick, sign) values (:nick, :sign)",
{'nick': nick.lower(), 'sign': sign})
db.execute("insert or replace into horoscope(nick, sign) values (?,?)",
(nick.lower(), sign))
db.commit()
return result

View file

@ -1,4 +1,4 @@
from urllib.parse import urlencode
from urllib import urlencode
import re
from util import hook, http, timeformat

View file

@ -1,4 +1,4 @@
import urllib.parse
import urlparse
from util import hook, http, urlnorm
@ -8,15 +8,14 @@ def isup(inp):
"""isup -- uses isup.me to see if a site is up or not"""
# slightly overcomplicated, esoteric URL parsing
scheme, auth, path, query, fragment = urllib.parse.urlsplit(inp.strip())
scheme, auth, path, query, fragment = urlparse.urlsplit(inp.strip())
domain = auth.encode('utf-8') or path.encode('utf-8')
url = urlnorm.normalize(domain, assume_scheme="http")
try:
soup = http.get_soup('http://isup.me/' + domain)
except http.HTTPError as xxx_todo_changeme:
http.URLError = xxx_todo_changeme
except http.HTTPError, http.URLError:
return "Could not get status."
content = soup.find('div').text.strip()

View file

@ -26,7 +26,7 @@ def kill(inp, action=None, nick=None, conn=None, notice=None):
"user": target
}
with open("./data/kills.json") as f:
with open("plugins/data/kills.json") as f:
generator = get_generator(f.read(), variables)
# act out the message

View file

@ -25,8 +25,8 @@ def lastfm(inp, nick='', db=None, bot=None, notice=None):
db.execute("create table if not exists lastfm(nick primary key, acc)")
if not user:
user = db.execute("select acc from lastfm where nick=lower(:nick)",
{'nick': nick}).fetchone()
user = db.execute("select acc from lastfm where nick=lower(?)",
(nick,)).fetchone()
if not user:
notice(lastfm.__doc__)
return
@ -36,10 +36,10 @@ def lastfm(inp, nick='', db=None, bot=None, notice=None):
api_key=api_key, user=user, limit=1)
if 'error' in response:
return "Error: {}.".format(response["message"])
return u"Error: {}.".format(response["message"])
if not "track" in response["recenttracks"] or len(response["recenttracks"]["track"]) == 0:
return 'No recent tracks for user "{}" found.'.format(user)
return u'No recent tracks for user "{}" found.'.format(user)
tracks = response["recenttracks"]["track"]
@ -66,18 +66,18 @@ def lastfm(inp, nick='', db=None, bot=None, notice=None):
album = track["album"]["#text"]
artist = track["artist"]["#text"]
out = '{} {} "{}"'.format(user, status, title)
out = u'{} {} "{}"'.format(user, status, title)
if artist:
out += " by \x02{}\x0f".format(artist)
out += u" by \x02{}\x0f".format(artist)
if album:
out += " from the album \x02{}\x0f".format(album)
out += u" from the album \x02{}\x0f".format(album)
# append ending based on what type it was
out += ending
if inp and not dontsave:
db.execute("insert or replace into lastfm(nick, acc) values "
"(:nick, :account)", {'nick': nick.lower(), 'account': user})
db.execute("insert or replace into lastfm(nick, acc) values (?,?)",
(nick.lower(), user))
db.commit()
return out

View file

@ -6,7 +6,7 @@ from util import hook, web, http
def lmgtfy(inp):
"""lmgtfy [phrase] - Posts a google link for the specified phrase"""
link = "http://lmgtfy.com/?q={}".format(http.quote_plus(inp))
link = u"http://lmgtfy.com/?q={}".format(http.quote_plus(inp))
try:
return web.isgd(link)

View file

@ -3,7 +3,6 @@ log.py: written by Scaevolus 2009
"""
import os
import sys
import codecs
import time
import re
@ -89,12 +88,12 @@ def get_log_fd(dir, server, chan):
return fd
#@hook.singlethread
@hook.singlethread
@hook.event('*')
def log(paraml, input=None, bot=None):
timestamp = gmtime(timestamp_format)
fd = get_log_fd(bot.data_dir, input.server, 'raw')
fd = get_log_fd(bot.persist_dir, input.server, 'raw')
fd.write(timestamp + ' ' + input.raw + '\n')
if input.command == 'QUIT': # these are temporary fixes until proper
@ -108,9 +107,7 @@ def log(paraml, input=None, bot=None):
return
if input.chan:
fd = get_log_fd(bot.data_dir, input.server, input.chan)
fd = get_log_fd(bot.persist_dir, input.server, input.chan)
fd.write(timestamp + ' ' + beau + '\n')
out = "{} {} {}".format(timestamp, input.chan, beau)
bot.logger.debug(out)
print timestamp, input.chan, beau.encode('utf8', 'ignore')

View file

@ -1,7 +1,7 @@
# metacritic.com scraper
import re
from urllib.error import HTTPError
from urllib2 import HTTPError
from util import hook, http

View file

@ -56,7 +56,7 @@ def plugin_random():
while not results:
plugin_number = random.randint(1, count_total)
print("trying {}".format(plugin_number))
print "trying {}".format(plugin_number)
try:
results = http.get_json(random_url.format(plugin_number))
except (http.HTTPError, http.URLError) as e:
@ -84,7 +84,7 @@ def format_output(data):
description = text.truncate_str(data['description'], 30)
url = data['website']
authors = data['authors'][0]
authors = authors[0] + "\u200b" + authors[1:]
authors = authors[0] + u"\u200b" + authors[1:]
stage = data['stage']
current_version = data['versions'][0]
@ -97,11 +97,11 @@ def format_output(data):
link = web.try_isgd(current_version['link'])
if description:
line_a = "\x02{}\x02, by \x02{}\x02 - {} - ({}) \x02{}".format(name, authors, description, stage, url)
line_a = u"\x02{}\x02, by \x02{}\x02 - {} - ({}) \x02{}".format(name, authors, description, stage, url)
else:
line_a = "\x02{}\x02, by \x02{}\x02 ({}) \x02{}".format(name, authors, stage, url)
line_a = u"\x02{}\x02, by \x02{}\x02 ({}) \x02{}".format(name, authors, stage, url)
line_b = "Last release: \x02v{}\x02 for \x02{}\x02 at {} \x02{}\x02".format(version_number, bukkit_versions,
line_b = u"Last release: \x02v{}\x02 for \x02{}\x02 at {} \x02{}\x02".format(version_number, bukkit_versions,
last_update, link)
return line_a, line_b

View file

@ -24,7 +24,7 @@ class Recipe(object):
return self.line
with open("./data/recipes.txt") as f:
with open("plugins/data/recipes.txt") as f:
for line in f.readlines():
if line.startswith("//"):
continue
@ -39,7 +39,7 @@ with open("./data/recipes.txt") as f:
ids = []
with open("./data/itemids.txt") as f:
with open("plugins/data/itemids.txt") as f:
for line in f.readlines():
if line.startswith("//"):
continue

View file

@ -13,12 +13,12 @@ except ImportError:
has_dns = False
mc_colors = [('\xa7f', '\x0300'), ('\xa70', '\x0301'), ('\xa71', '\x0302'), ('\xa72', '\x0303'),
('\xa7c', '\x0304'), ('\xa74', '\x0305'), ('\xa75', '\x0306'), ('\xa76', '\x0307'),
('\xa7e', '\x0308'), ('\xa7a', '\x0309'), ('\xa73', '\x0310'), ('\xa7b', '\x0311'),
('\xa71', '\x0312'), ('\xa7d', '\x0313'), ('\xa78', '\x0314'), ('\xa77', '\x0315'),
('\xa7l', '\x02'), ('\xa79', '\x0310'), ('\xa7o', '\t'), ('\xa7m', '\x13'),
('\xa7r', '\x0f'), ('\xa7n', '\x15')]
mc_colors = [(u'\xa7f', u'\x0300'), (u'\xa70', u'\x0301'), (u'\xa71', u'\x0302'), (u'\xa72', u'\x0303'),
(u'\xa7c', u'\x0304'), (u'\xa74', u'\x0305'), (u'\xa75', u'\x0306'), (u'\xa76', u'\x0307'),
(u'\xa7e', u'\x0308'), (u'\xa7a', u'\x0309'), (u'\xa73', u'\x0310'), (u'\xa7b', u'\x0311'),
(u'\xa71', u'\x0312'), (u'\xa7d', u'\x0313'), (u'\xa78', u'\x0314'), (u'\xa77', u'\x0315'),
(u'\xa7l', u'\x02'), (u'\xa79', u'\x0310'), (u'\xa7o', u'\t'), (u'\xa7m', u'\x13'),
(u'\xa7r', u'\x0f'), (u'\xa7n', u'\x15')]
## EXCEPTIONS
@ -98,9 +98,9 @@ def mcping_modern(host, port):
try:
version = data["version"]["name"]
try:
desc = " ".join(data["description"]["text"].split())
desc = u" ".join(data["description"]["text"].split())
except TypeError:
desc = " ".join(data["description"].split())
desc = u" ".join(data["description"].split())
max_players = data["players"]["max"]
online = data["players"]["online"]
except Exception as e:
@ -136,10 +136,10 @@ def mcping_legacy(host, port):
length = struct.unpack('!h', sock.recv(2))[0]
values = sock.recv(length * 2).decode('utf-16be')
data = values.split('\x00') # try to decode data using new format
data = values.split(u'\x00') # try to decode data using new format
if len(data) == 1:
# failed to decode data, server is using old format
data = values.split('\xa7')
data = values.split(u'\xa7')
output = {
"motd": format_colors(" ".join(data[0].split())),
"motd_raw": data[0],
@ -199,17 +199,17 @@ def parse_input(inp):
def format_colors(motd):
for original, replacement in mc_colors:
motd = motd.replace(original, replacement)
motd = motd.replace("\xa7k", "")
motd = motd.replace(u"\xa7k", "")
return motd
def format_output(data):
if data["version"]:
return "{motd}\x0f - {version}\x0f - {players}/{players_max}" \
" players.".format(**data).replace("\n", "\x0f - ")
return u"{motd}\x0f - {version}\x0f - {players}/{players_max}" \
u" players.".format(**data).replace("\n", u"\x0f - ")
else:
return "{motd}\x0f - {players}/{players_max}" \
" players.".format(**data).replace("\n", "\x0f - ")
return u"{motd}\x0f - {players}/{players_max}" \
u" players.".format(**data).replace("\n", u"\x0f - ")
@hook.command

View file

@ -21,7 +21,7 @@ def mcstatus(inp):
green = []
yellow = []
red = []
for server, status in list(data.items()):
for server, status in data.items():
if status == "green":
green.append(server)
elif status == "yellow":

View file

@ -87,15 +87,15 @@ def mcuser(inp):
profile["lt"] = ", legacy" if profile["legacy"] else ""
if profile["paid"]:
return "The account \x02{name}\x02 ({id}{lt}) exists. It is a \x02paid\x02" \
" account.".format(**profile)
return u"The account \x02{name}\x02 ({id}{lt}) exists. It is a \x02paid\x02" \
u" account.".format(**profile)
else:
return "The account \x02{name}\x02 ({id}{lt}) exists. It \x034\x02is NOT\x02\x0f a paid" \
" account.".format(**profile)
return u"The account \x02{name}\x02 ({id}{lt}) exists. It \x034\x02is NOT\x02\x0f a paid" \
u" account.".format(**profile)
elif name_status == "free":
return "The account \x02{}\x02 does not exist.".format(user)
return u"The account \x02{}\x02 does not exist.".format(user)
elif name_status == "invalid":
return "The name \x02{}\x02 contains invalid characters.".format(user)
return u"The name \x02{}\x02 contains invalid characters.".format(user)
else:
# if you see this, panic
return "Unknown Error."

View file

@ -45,7 +45,7 @@ def mcwiki(inp):
summary = " ".join(p.text_content().splitlines())
summary = re.sub("\[\d+\]", "", summary)
summary = text.truncate_str(summary, 200)
return "{} :: {}".format(summary, url)
return u"{} :: {}".format(summary, url)
# this shouldn't happen
return "Unknown Error."

183
disabled_stuff/mtg.py Normal file
View file

@ -0,0 +1,183 @@
import re
from util import hook, http
@hook.command
def mtg(inp):
".mtg <name> -- Gets information about Magic the Gathering card <name>."
url = 'http://magiccards.info/query?v=card&s=cname'
h = http.get_html(url, q=inp)
name = h.find('body/table/tr/td/span/a')
if name is None:
return "No cards found :("
card = name.getparent().getparent().getparent()
type = card.find('td/p').text.replace('\n', '')
# this is ugly
text = http.html.tostring(card.xpath("//p[@class='ctext']/b")[0])
text = text.replace('<br>', '$')
text = http.html.fromstring(text).text_content()
text = re.sub(r'(\w+\s*)\$+(\s*\w+)', r'\1. \2', text)
text = text.replace('$', ' ')
text = re.sub(r'\(.*?\)', '', text) # strip parenthetical explanations
text = re.sub(r'\.(\S)', r'. \1', text) # fix spacing
printings = card.find('td/small').text_content()
printings = re.search(r'Editions:(.*)Languages:', printings).group(1)
printings = re.findall(r'\s*(.+?(?: \([^)]+\))*) \((.*?)\)',
' '.join(printings.split()))
printing_out = ', '.join('%s (%s)' % (set_abbrevs.get(x[0], x[0]),
rarity_abbrevs.get(x[1], x[1]))
for x in printings)
name.make_links_absolute(base_url=url)
link = name.attrib['href']
name = name.text_content().strip()
type = type.strip()
text = ' '.join(text.split())
return ' | '.join((name, type, text, printing_out, link))
set_abbrevs = {
'15th Anniversary': '15ANN',
'APAC Junior Series': 'AJS',
'Alara Reborn': 'ARB',
'Alliances': 'AI',
'Anthologies': 'AT',
'Antiquities': 'AQ',
'Apocalypse': 'AP',
'Arabian Nights': 'AN',
'Arena League': 'ARENA',
'Asia Pacific Land Program': 'APAC',
'Battle Royale': 'BR',
'Battle Royale Box Set': 'BRB',
'Beatdown': 'BTD',
'Beatdown Box Set': 'BTD',
'Betrayers of Kamigawa': 'BOK',
'Celebration Cards': 'UQC',
'Champions of Kamigawa': 'CHK',
'Champs': 'CP',
'Chronicles': 'CH',
'Classic Sixth Edition': '6E',
'Coldsnap': 'CS',
'Coldsnap Theme Decks': 'CSTD',
'Conflux': 'CFX',
'Core Set - Eighth Edition': '8E',
'Core Set - Ninth Edition': '9E',
'Darksteel': 'DS',
'Deckmasters': 'DM',
'Dissension': 'DI',
'Dragon Con': 'DRC',
'Duel Decks: Divine vs. Demonic': 'DVD',
'Duel Decks: Elves vs. Goblins': 'EVG',
'Duel Decks: Garruk vs. Liliana': 'GVL',
'Duel Decks: Jace vs. Chandra': 'JVC',
'Eighth Edition': '8ED',
'Eighth Edition Box Set': '8EB',
'European Land Program': 'EURO',
'Eventide': 'EVE',
'Exodus': 'EX',
'Fallen Empires': 'FE',
'Fifth Dawn': '5DN',
'Fifth Edition': '5E',
'Fourth Edition': '4E',
'Friday Night Magic': 'FNMP',
'From the Vault: Dragons': 'FVD',
'From the Vault: Exiled': 'FVE',
'Future Sight': 'FUT',
'Gateway': 'GRC',
'Grand Prix': 'GPX',
'Guildpact': 'GP',
'Guru': 'GURU',
'Happy Holidays': 'HHO',
'Homelands': 'HL',
'Ice Age': 'IA',
'Introductory Two-Player Set': 'ITP',
'Invasion': 'IN',
'Judge Gift Program': 'JR',
'Judgment': 'JU',
'Junior Series': 'JSR',
'Legend Membership': 'DCILM',
'Legends': 'LG',
'Legions': 'LE',
'Limited Edition (Alpha)': 'LEA',
'Limited Edition (Beta)': 'LEB',
'Limited Edition Alpha': 'LEA',
'Limited Edition Beta': 'LEB',
'Lorwyn': 'LW',
'MTGO Masters Edition': 'MED',
'MTGO Masters Edition II': 'ME2',
'MTGO Masters Edition III': 'ME3',
'Magic 2010': 'M10',
'Magic Game Day Cards': 'MGDC',
'Magic Player Rewards': 'MPRP',
'Magic Scholarship Series': 'MSS',
'Magic: The Gathering Launch Parties': 'MLP',
'Media Inserts': 'MBP',
'Mercadian Masques': 'MM',
'Mirage': 'MR',
'Mirrodin': 'MI',
'Morningtide': 'MT',
'Multiverse Gift Box Cards': 'MGBC',
'Nemesis': 'NE',
'Ninth Edition Box Set': '9EB',
'Odyssey': 'OD',
'Onslaught': 'ON',
'Planar Chaos': 'PC',
'Planechase': 'PCH',
'Planeshift': 'PS',
'Portal': 'PO',
'Portal Demogame': 'POT',
'Portal Second Age': 'PO2',
'Portal Three Kingdoms': 'P3K',
'Premium Deck Series: Slivers': 'PDS',
'Prerelease Events': 'PTC',
'Pro Tour': 'PRO',
'Prophecy': 'PR',
'Ravnica: City of Guilds': 'RAV',
'Release Events': 'REP',
'Revised Edition': 'RV',
'Saviors of Kamigawa': 'SOK',
'Scourge': 'SC',
'Seventh Edition': '7E',
'Shadowmoor': 'SHM',
'Shards of Alara': 'ALA',
'Starter': 'ST',
'Starter 1999': 'S99',
'Starter 2000 Box Set': 'ST2K',
'Stronghold': 'SH',
'Summer of Magic': 'SOM',
'Super Series': 'SUS',
'Tempest': 'TP',
'Tenth Edition': '10E',
'The Dark': 'DK',
'Time Spiral': 'TS',
'Time Spiral Timeshifted': 'TSTS',
'Torment': 'TR',
'Two-Headed Giant Tournament': 'THGT',
'Unglued': 'UG',
'Unhinged': 'UH',
'Unhinged Alternate Foils': 'UHAA',
'Unlimited Edition': 'UN',
"Urza's Destiny": 'UD',
"Urza's Legacy": 'UL',
"Urza's Saga": 'US',
'Visions': 'VI',
'Weatherlight': 'WL',
'Worlds': 'WRL',
'WotC Online Store': 'WOTC',
'Zendikar': 'ZEN'}
rarity_abbrevs = {
'Land': 'L',
'Common': 'C',
'Uncommon': 'UC',
'Rare': 'R',
'Special': 'S',
'Mythic Rare': 'MR'}

View file

@ -0,0 +1,115 @@
# BING translation plugin by Lukeroge and neersighted
from util import hook
from util import http
import re
import htmlentitydefs
import mygengo
gengo = mygengo.MyGengo(
public_key='PlwtF1CZ2tu27IdX_SXNxTFmfN0j|_-pJ^Rf({O-oLl--r^QM4FygRdt^jusSSDE',
private_key='wlXpL=SU[#JpPu[dQaf$v{S3@rg[=95$$TA(k$sb3_6~B_zDKkTbd4#hXxaorIae',
sandbox=False,
)
def gengo_translate(text, source, target):
try:
translation = gengo.postTranslationJob(job={
'type': 'text',
'slug': 'Translating '+source+' to '+target+' with the myGengo API',
'body_src': text,
'lc_src': source,
'lc_tgt': target,
'tier': 'machine',
})
translated = translation['response']['job']['body_tgt']
return u"(%s > %s) %s" % (source, target, translated)
except mygengo.MyGengoError:
return "error: could not translate"
def match_language(fragment):
fragment = fragment.lower()
for short, _ in lang_pairs:
if fragment in short.lower().split():
return short.split()[0]
for short, full in lang_pairs:
if fragment in full.lower():
return short.split()[0]
return None
@hook.command
def translate(inp):
".translate <source language> <target language> <sentence> -- Translates <sentence> from <source language> to <target language> using MyGengo."
args = inp.split(' ')
sl = match_language(args[0])
tl = match_language(args[1])
txt = unicode(" ".join(args[2:]))
if sl and tl:
return unicode(gengo_translate(txt, sl, tl))
else:
return "error: translate could not reliably determine one or both languages"
languages = 'ja fr de ko ru zh'.split()
language_pairs = zip(languages[:-1], languages[1:])
lang_pairs = [
("no", "Norwegian"),
("it", "Italian"),
("ht", "Haitian Creole"),
("af", "Afrikaans"),
("sq", "Albanian"),
("ar", "Arabic"),
("hy", "Armenian"),
("az", "Azerbaijani"),
("eu", "Basque"),
("be", "Belarusian"),
("bg", "Bulgarian"),
("ca", "Catalan"),
("zh-CN zh", "Chinese"),
("hr", "Croatian"),
("cs cz", "Czech"),
("da dk", "Danish"),
("nl", "Dutch"),
("en", "English"),
("et", "Estonian"),
("tl", "Filipino"),
("fi", "Finnish"),
("fr", "French"),
("gl", "Galician"),
("ka", "Georgian"),
("de", "German"),
("el", "Greek"),
("ht", "Haitian Creole"),
("iw", "Hebrew"),
("hi", "Hindi"),
("hu", "Hungarian"),
("is", "Icelandic"),
("id", "Indonesian"),
("ga", "Irish"),
("it", "Italian"),
("ja jp jpn", "Japanese"),
("ko", "Korean"),
("lv", "Latvian"),
("lt", "Lithuanian"),
("mk", "Macedonian"),
("ms", "Malay"),
("mt", "Maltese"),
("no", "Norwegian"),
("fa", "Persian"),
("pl", "Polish"),
("pt", "Portuguese"),
("ro", "Romanian"),
("ru", "Russian"),
("sr", "Serbian"),
("sk", "Slovak"),
("sl", "Slovenian"),
("es", "Spanish"),
("sw", "Swahili"),
("sv", "Swedish"),
("th", "Thai"),
("tr", "Turkish"),
("uk", "Ukrainian"),
("ur", "Urdu"),
("vi", "Vietnamese"),
("cy", "Welsh"),
("yi", "Yiddish")
]

View file

@ -14,12 +14,12 @@ def get_generator(_json):
@hook.command(autohelp=False)
def namegen(input, instance, bot):
def namegen(inp, notice=None):
"""namegen [generator] -- Generates some names using the chosen generator.
'namegen list' will display a list of all generators."""
# clean up the input
inp = input.text.strip().lower()
inp = inp.strip().lower()
# get a list of available name generators
files = os.listdir(GEN_DIR)
@ -33,7 +33,7 @@ def namegen(input, instance, bot):
if inp == "list":
message = "Available generators: "
message += text.get_text_list(all_modules, 'and')
input.notice(message)
notice(message)
return
if inp:

View file

@ -46,18 +46,18 @@ def format_item(item, show_url=True):
tags.append("\x02Featured\x02")
if item["IsShellShockerItem"]:
tags.append("\x02SHELL SHOCKER\u00AE\x02")
tags.append(u"\x02SHELL SHOCKER\u00AE\x02")
# join all the tags together in a comma separated string ("tag1, tag2, tag3")
tag_text = ", ".join(tags)
tag_text = u", ".join(tags)
if show_url:
# create the item URL and shorten it
url = web.try_isgd(ITEM_URL.format(item["NeweggItemNumber"]))
return "\x02{}\x02 ({}) - {} - {} - {}".format(title, price, rating,
return u"\x02{}\x02 ({}) - {} - {} - {}".format(title, price, rating,
tag_text, url)
else:
return "\x02{}\x02 ({}) - {} - {}".format(title, price, rating,
return u"\x02{}\x02 ({}) - {} - {}".format(title, price, rating,
tag_text)

View file

@ -15,7 +15,7 @@ def test(s):
def newgrounds_url(match):
location = match.group(4).split("/")[-1]
if not test(location):
print("Not a valid Newgrounds portal ID. Example: http://www.newgrounds.com/portal/view/593993")
print "Not a valid Newgrounds portal ID. Example: http://www.newgrounds.com/portal/view/593993"
return None
soup = http.get_soup("http://www.newgrounds.com/portal/view/" + location)
@ -31,7 +31,7 @@ def newgrounds_url(match):
# get rating
try:
rating_info = soup.find('dd', {'class': 'star-variable'})['title'].split("Stars &ndash;")[0].strip()
rating = " - rated \x02{}\x02/\x025.0\x02".format(rating_info)
rating = u" - rated \x02{}\x02/\x025.0\x02".format(rating_info)
except:
rating = ""

View file

@ -29,7 +29,7 @@ def password(inp, notice=None):
# add numbers
if "numeric" in inp or "number" in inp:
okay = okay + [str(x) for x in range(0, 10)]
okay = okay + [str(x) for x in xrange(0, 10)]
# add symbols
if "symbol" in inp:

View file

@ -36,5 +36,3 @@ def pre(inp):
size = ''
return '{} - {}{} - {} ({} ago)'.format(section, name, size, date_string, since)
print(pre("top gear"))

View file

@ -1,4 +1,4 @@
import urllib.request, urllib.parse, urllib.error
import urllib
import json
import re
@ -11,7 +11,7 @@ def getdata(inp, types, api_key, api_secret):
consumer = oauth.Consumer(api_key, api_secret)
client = oauth.Client(consumer)
response = client.request('http://api.rdio.com/1/', 'POST',
urllib.parse.urlencode({'method': 'search', 'query': inp, 'types': types, 'count': '1'}))
urllib.urlencode({'method': 'search', 'query': inp, 'types': types, 'count': '1'}))
data = json.loads(response[1])
return data
@ -34,16 +34,16 @@ def rdio(inp, bot=None):
artist = info['artist']
album = info['album']
url = info['shortUrl']
return "\x02{}\x02 by \x02{}\x02 - {} {}".format(name, artist, album, url)
return u"\x02{}\x02 by \x02{}\x02 - {} {}".format(name, artist, album, url)
elif 'artist' in info and not 'album' in info: # Album
name = info['name']
artist = info['artist']
url = info['shortUrl']
return "\x02{}\x02 by \x02{}\x02 - {}".format(name, artist, url)
return u"\x02{}\x02 by \x02{}\x02 - {}".format(name, artist, url)
else: # Artist
name = info['name']
url = info['shortUrl']
return "\x02{}\x02 - {}".format(name, url)
return u"\x02{}\x02 - {}".format(name, url)
@hook.command
@ -62,7 +62,7 @@ def rdiot(inp, bot=None):
artist = info['artist']
album = info['album']
url = info['shortUrl']
return "\x02{}\x02 by \x02{}\x02 - {} - {}".format(name, artist, album, url)
return u"\x02{}\x02 by \x02{}\x02 - {} - {}".format(name, artist, album, url)
@hook.command
@ -79,7 +79,7 @@ def rdioar(inp, bot=None):
return "No results."
name = info['name']
url = info['shortUrl']
return "\x02{}\x02 - {}".format(name, url)
return u"\x02{}\x02 - {}".format(name, url)
@hook.command
@ -97,7 +97,7 @@ def rdioal(inp, bot=None):
name = info['name']
artist = info['artist']
url = info['shortUrl']
return "\x02{}\x02 by \x02{}\x02 - {}".format(name, artist, url)
return u"\x02{}\x02 by \x02{}\x02 - {}".format(name, artist, url)
rdio_re = (r'(.*:)//(rd.io|www.rdio.com|rdio.com)(:[0-9]+)?(.*)', re.I)
@ -113,7 +113,7 @@ def rdio_url(match, bot=None):
consumer = oauth.Consumer(api_key, api_secret)
client = oauth.Client(consumer)
response = client.request('http://api.rdio.com/1/', 'POST',
urllib.parse.urlencode({'method': 'getObjectFromUrl', 'url': url}))
urllib.urlencode({'method': 'getObjectFromUrl', 'url': url}))
data = json.loads(response[1])
info = data['result']
if 'name' in info:
@ -121,11 +121,11 @@ def rdio_url(match, bot=None):
name = info['name']
artist = info['artist']
album = info['album']
return "Rdio track: \x02{}\x02 by \x02{}\x02 - {}".format(name, artist, album)
return u"Rdio track: \x02{}\x02 by \x02{}\x02 - {}".format(name, artist, album)
elif 'artist' in info and not 'album' in info: # Album
name = info['name']
artist = info['artist']
return "Rdio album: \x02{}\x02 by \x02{}\x02".format(name, artist)
return u"Rdio album: \x02{}\x02 by \x02{}\x02".format(name, artist)
else: # Artist
name = info['name']
return "Rdio artist: \x02{}\x02".format(name)
return u"Rdio artist: \x02{}\x02".format(name)

Some files were not shown because too many files have changed in this diff Show more