Compare commits
57 commits
refresh
...
ChaosChemn
Author | SHA1 | Date | |
---|---|---|---|
![]() |
358b415566 | ||
![]() |
3e1ea6ac4e | ||
![]() |
48b627ad4b | ||
![]() |
1f1ff649c9 | ||
8344fe7693 | |||
a6992d5d1a | |||
![]() |
21db4efd98 | ||
![]() |
8941d59dc2 | ||
09ce708709 | |||
![]() |
29cc95f0ad | ||
4a02f545ea | |||
![]() |
28176ad2ad | ||
![]() |
651d4a953e | ||
![]() |
80711e1278 | ||
a88d5ef0bd | |||
827c5c73e8 | |||
![]() |
580415ed76 | ||
c562a2c7ae | |||
![]() |
4b5e86c237 | ||
![]() |
11687a9ad0 | ||
296de07e14 | |||
dfbead6946 | |||
![]() |
e30eca5ebd | ||
![]() |
2a3c9f52dc | ||
![]() |
f88c3267d4 | ||
![]() |
2b18eaac5a | ||
0650046d18 | |||
![]() |
4e386c9008 | ||
![]() |
05bfb9099a | ||
![]() |
bfa3fb6e74 | ||
![]() |
d415d0fc69 | ||
![]() |
433d97d276 | ||
![]() |
121715115a | ||
![]() |
620de651ce | ||
![]() |
3f205eaafa | ||
![]() |
59cd8380d2 | ||
![]() |
7cce9bf27e | ||
![]() |
0ba2001b62 | ||
![]() |
63fc042027 | ||
![]() |
9421d8160d | ||
![]() |
4b4ac2d918 | ||
![]() |
05a68faf1d | ||
![]() |
40328cf24f | ||
![]() |
fb471bee17 | ||
![]() |
48ab7417b8 | ||
![]() |
97a3283eff | ||
![]() |
6d147e1677 | ||
![]() |
c99c44616c | ||
![]() |
1062e69e56 | ||
![]() |
5ca45fea4b | ||
![]() |
04af154c5b | ||
![]() |
f82041fc87 | ||
![]() |
a027bd780f | ||
![]() |
a611e0df45 | ||
![]() |
e49ec9a9c9 | ||
![]() |
4652ed90a3 | ||
![]() |
ff3ef576b7 |
232 changed files with 21345 additions and 1396 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -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
|
||||
|
|
26
README.md
26
README.md
|
@ -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.
|
||||
|
||||
|
|
92
cloudbot.py
92
cloudbot.py
|
@ -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)
|
||||
|
||||
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:
|
||||
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.'
|
||||
|
||||
# watch to see if the bot stops running or needs a restart
|
||||
while True:
|
||||
if cloudbot.running:
|
||||
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)
|
||||
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
|
||||
else:
|
||||
break
|
103
config.default
103
config.default
|
@ -1,49 +1,26 @@
|
|||
{
|
||||
"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": {
|
||||
"admins": {
|
||||
"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"]
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
|
||||
},
|
||||
"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": "."
|
||||
}
|
||||
],
|
||||
"api_keys":
|
||||
{
|
||||
},
|
||||
"disabled_plugins": [],
|
||||
"disabled_commands": [],
|
||||
"acls": {},
|
||||
"api_keys": {
|
||||
"tvdb": "",
|
||||
"wolframalpha": "",
|
||||
"lastfm": "",
|
||||
|
@ -58,5 +35,43 @@
|
|||
"rdio_key": "",
|
||||
"rdio_secret": ""
|
||||
},
|
||||
"disabled_plugins": []
|
||||
"permissions": {
|
||||
"admins": {
|
||||
"perms": [
|
||||
"adminonly",
|
||||
"addfactoid",
|
||||
"delfactoid",
|
||||
"ignore",
|
||||
"botcontrol",
|
||||
"permissions_users",
|
||||
"op"
|
||||
],
|
||||
"users": [
|
||||
"examplea!user@example.com",
|
||||
"exampleb!user@example.com"
|
||||
]
|
||||
},
|
||||
"moderators": {
|
||||
"perms": [
|
||||
"addfactoid",
|
||||
"delfactoid",
|
||||
"ignore"
|
||||
],
|
||||
"users": [
|
||||
"stummi!~Stummi@stummi.org"
|
||||
]
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"factoids": {
|
||||
"prefix": false
|
||||
},
|
||||
"ignore": {
|
||||
"ignored": []
|
||||
}
|
||||
},
|
||||
"censored_strings": [
|
||||
"mypass",
|
||||
"mysecret"
|
||||
]
|
||||
}
|
||||
|
|
175
core/bot.py
175
core/bot.py
|
@ -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)
|
|
@ -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()
|
||||
|
||||
|
||||
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)
|
||||
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()
|
||||
|
||||
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()
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
|
|
@ -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:
|
||||
|
|
316
core/irc.py
316
core/irc.py
|
@ -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
|
||||
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)
|
||||
|
@ -88,49 +139,59 @@ class SSLReceiveThread(ReceiveThread):
|
|||
|
||||
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)
|
||||
#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
|
||||
|
@ -147,109 +208,11 @@ class ParseThread(threading.Thread):
|
|||
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,
|
||||
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)
|
||||
|
|
153
core/loader.py
153
core/loader.py
|
@ -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)
|
121
core/main.py
121
core/main.py
|
@ -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)
|
||||
|
|
|
@ -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
160
core/reload.py
Normal 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
|
|
@ -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
121
disabled_stuff/cleverbot.py
Normal 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
126
disabled_stuff/cloudbot.sh
Normal 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
|
37
disabled_stuff/correction.py
Normal file
37
disabled_stuff/correction.py
Normal 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)
|
||||
|
|
@ -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']))
|
BIN
disabled_stuff/data/GeoLiteCity.dat
Normal file
BIN
disabled_stuff/data/GeoLiteCity.dat
Normal file
Binary file not shown.
|
@ -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)
|
|
@ -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()
|
||||
|
|
@ -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:
|
|
@ -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))
|
|
@ -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
|
|
@ -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)
|
||||
|
||||
|
@ -151,5 +155,8 @@ def factoid(inp, message=None, db=None, bot=None, action=None, conn=None, input=
|
|||
message(http.get(url))
|
||||
except http.HttpError:
|
||||
message("Could not fetch URL.")
|
||||
else:
|
||||
if prefix_on:
|
||||
message("\x02[{}]:\x02 {}".format(factoid_id, result))
|
||||
else:
|
||||
message(result)
|
|
@ -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:
|
|
@ -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
13
disabled_stuff/freddy.py
Normal 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))
|
||||
|
|
@ -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)
|
|
@ -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!"
|
||||
|
|
@ -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)
|
|
@ -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
|
||||
|
||||
|
|
@ -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):
|
|
@ -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])
|
|
@ -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
|
|
@ -1,4 +1,4 @@
|
|||
from urllib.parse import urlencode
|
||||
from urllib import urlencode
|
||||
import re
|
||||
|
||||
from util import hook, http, timeformat
|
|
@ -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()
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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')
|
|
@ -1,7 +1,7 @@
|
|||
# metacritic.com scraper
|
||||
|
||||
import re
|
||||
from urllib.error import HTTPError
|
||||
from urllib2 import HTTPError
|
||||
|
||||
from util import hook, http
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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":
|
|
@ -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."
|
|
@ -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
183
disabled_stuff/mtg.py
Normal 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'}
|
115
disabled_stuff/mygengo_translate.py
Normal file
115
disabled_stuff/mygengo_translate.py
Normal 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")
|
||||
]
|
|
@ -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:
|
|
@ -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)
|
||||
|
||||
|
|
@ -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 –")[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 = ""
|
||||
|
|
@ -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:
|
|
@ -36,5 +36,3 @@ def pre(inp):
|
|||
size = ''
|
||||
|
||||
return '{} - {}{} - {} ({} ago)'.format(section, name, size, date_string, since)
|
||||
|
||||
print(pre("top gear"))
|
|
@ -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
Reference in a new issue