''' 8ball: provide answers from the void beyond. ''' import os.path import random import fortune from tombot.registry import Command, get_easy_logger, Subscribe, BOT_START LOGGER = get_easy_logger('plugins.fortune') FORTUNE_FILES = [] SPECIALS = {} @Command('fortune', 'fortune') def fortune_cb(bot, *args, **kwargs): ''' Return a random quote from one of the quote files. ''' try: source = random.choice(FORTUNE_FILES) return fortune.get_random_fortune(source) except ValueError as ex: LOGGER.error('Fortune failed: %s', ex) return _('Be the quote you want to see on a wall.\n -- Error 20XX') @Subscribe(BOT_START) @Command('loadfortunes', 'fortune', hidden=True) def load_fortunes_cb(bot, message=None, *args, **kwargs): ''' (Re)load all fortune and specials files from their directories.
''' ABAS: Automated Birthday Announcement System ''' from apscheduler.jobstores.base import JobLookupError from tombot.registry import get_easy_logger, Subscribe, BOT_START, BOT_SHUTDOWN from tombot.rpc import remote_send LOGGER = get_easy_logger('plugins.abas') def announce_bday(name, recipient): ''' Send a congratulation for name to recipient. ''' LOGGER.info('Congratulating %s', name) body = 'Gefeliciteerd, {}!'.format(name) remote_send(body, recipient) @Subscribe(BOT_START) def abas_register_cb(bot, *args, **kwargs): ''' Add jobs to the scheduler for all birthdays. ''' LOGGER.info('Registering ABAs.') try: bot.cursor.execute( 'SELECT primary_nick,bday FROM users WHERE bday IS NOT NULL') except TypeError: LOGGER.error('Invalid date found, fix your database!') return results = bot.cursor.fetchall() for person in results: LOGGER.info('Scheduling ABA for %s', person[0]) bot.scheduler.add_job(announce_bday,
Provides a command for scheduling reminders. Never forget. ''' import datetime import dateutil.parser from yowsup.layers.protocol_messages.protocolentities \ import TextMessageProtocolEntity from tombot.registry import Command, get_easy_logger from tombot.helper_functions import extract_query, determine_sender, reply_directly import tombot.datefinder as datefinder import tombot.rpc as rpc LOGGER = get_easy_logger('plugins.reminder') @Command(['remind', 'remindme']) @reply_directly def addreminder_cb(bot, message, *args, **kwargs): ''' (Hopefully) sends user a message at the given time ''' body = extract_query(message) timespec = body.split()[0] try: trytime = dateutil.parser.parse(body, parserinfo=datefinder.BPI, fuzzy=True) except ValueError: trytime = datetime.datetime(1970, 1, 1) # job is dropped if no other date is found delta = None if timespec in datefinder.DURATION_MARKERS or datefinder.STRICT_CLOCK_REGEX.match(timespec): try: delta = datetime.datetime.now() + datefinder.find_timedelta(body)
Provides a command for scheduling reminders. Never forget. ''' import datetime import dateutil.parser from yowsup.layers.protocol_messages.protocolentities \ import TextMessageProtocolEntity from tombot.registry import Command, get_easy_logger from tombot.helper_functions import extract_query, determine_sender, reply_directly import tombot.datefinder as datefinder import tombot.rpc as rpc LOGGER = get_easy_logger('plugins.reminder') @Command(['remind', 'remindme']) @reply_directly def addreminder_cb(bot, message, *args, **kwargs): ''' (Hopefully) sends user a message at the given time ''' body = extract_query(message) timespec = body.split()[0] try: trytime = dateutil.parser.parse(body, parserinfo=datefinder.BPI, fuzzy=True) except ValueError: trytime = datetime.datetime( 1970, 1, 1) # job is dropped if no other date is found
''' Provides the cookie command, which originally was a test for unicode and now spouts cookie quotes. ''' import fortune from tombot.registry import Command, get_easy_logger from .fortune_plugin import SPECIALS LOGGER = get_easy_logger('plugins.cookie') @Command(['cookie', 'koekje', '\xf0\x9f\x8d\xaa'], 'fortune') def cookie_cb(bot, *args, **kwargs): ''' Return a cookie-related quote. ''' try: return fortune.get_random_fortune(SPECIALS['cookie.spc']) except KeyError: LOGGER.error('Specials file was not loaded!') return 'Error!\xf0\x9f\x8d\xaa'
''' Provides command for answering queries using the DuckDuckGo API. ''' import duckduckgo from tombot.registry import Command, get_easy_logger from tombot.helper_functions import extract_query LOGGER = get_easy_logger('plugins.duckduckgo') @Command(['duckduckgo', 'ddg', 'define'], 'info') def duckduckgo_cb(bot, message, *args, **kwargs): ''' Answer query using DuckDuckGo. ''' try: query = extract_query(message) return duckduckgo.get_zci(query) except ValueError: return 'Sorry, no results.' except AttributeError: return 'Sorry, no results.'
''' Provides commands to globally modify the bot's behaviour. ''' import logging import pydoc from .users_plugin import isadmin from tombot.registry import get_easy_logger, Command, Subscribe, BOT_START from tombot.registry import COMMAND_DICT, COMMAND_CATEGORIES from tombot.helper_functions import determine_sender, extract_query, reply_directly LOGGER = get_easy_logger('plugins.system') HELP_OVERVIEW = '' @Command('ping', 'system') def ping_cb(bot=None, message=None, *args, **kwargs): ''' Return 'pong' to indicate non-deadness. ''' return 'Pong' @Command('forcelog', 'system', hidden=True) def forcelog_cb(bot, message, *args, **kwargs): ''' Write a message to the root logger. ''' logging.info('Forcelog from %s: %s', message.getFrom(), message.getBody()) return @Command(['shutdown', 'halt'], 'system') def shutdown_cb(bot, message, *args, **kwargs): ''' Shut down the bot. ''' LOGGER.info('Stop message received from %s, content "%s"',
''' Provides a command for answering queries using the WolframAlpha API. ''' import os import urllib import wolframalpha from tombot.helper_functions import extract_query from tombot.registry import Command, get_easy_logger, Subscribe, BOT_START from yowsup.layers.protocol_chatstate.protocolentities \ import OutgoingChatstateProtocolEntity, ChatstateProtocolEntity LOGGER = get_easy_logger('plugins.wolframalpha') CLIENT = None @Command(['calc', 'calculate', 'bereken']) def wolfram_cb(bot, message, *args, **kwargs): ''' (Attempt to) answer query using the WolframAlpha API. Results may not be interpreted as you'd expect, open link for explanation. ''' if not CLIENT: return _('Not connected to WolframAlpha!') query = extract_query(message) LOGGER.debug('Query to WolframAlpha: %s', query) entity = OutgoingChatstateProtocolEntity( ChatstateProtocolEntity.STATE_TYPING, message.getFrom()) bot.toLower(entity)
''' Provides user and nickname management. ''' import datetime import sqlite3 import operator from tombot.helper_functions import determine_sender, extract_query, reply_directly from tombot.registry import Command, get_easy_logger LOGGER = get_easy_logger('plugins.users') IS_ID = operator.methodcaller('isdigit') # User @Command(['mynicks', 'lsnicks'], 'users') @reply_directly def list_own_nicks_cb(bot, message, *args, **kwargs): ''' List all your nicks and their id's. Nicks can be added using addnick, removed using rmnick. ''' sender = determine_sender(message) bot.cursor.execute('SELECT id,primary_nick FROM users WHERE jid = ?', (sender,)) result = bot.cursor.fetchone() if result is None: return 'Wie ben jij' userid = result[0] username = result[1] bot.cursor.execute('SELECT id,name FROM nicks WHERE jid = ?',
''' Provides user and nickname management. ''' import datetime import sqlite3 import operator from tombot.helper_functions import determine_sender, extract_query, reply_directly from tombot.registry import Command, get_easy_logger LOGGER = get_easy_logger('plugins.users') IS_ID = operator.methodcaller('isdigit') # User @Command(['mynicks', 'lsnicks'], 'users') @reply_directly def list_own_nicks_cb(bot, message, *args, **kwargs): ''' List all your nicks and their id's. Nicks can be added using addnick, removed using rmnick. ''' sender = determine_sender(message) bot.cursor.execute('SELECT id,primary_nick FROM users WHERE jid = ?', (sender, )) result = bot.cursor.fetchone() if result is None: return 'Wie ben jij' userid = result[0] username = result[1] bot.cursor.execute('SELECT id,name FROM nicks WHERE jid = ?', (sender, ))
''' 8ball: provide answers from the void beyond. ''' import os.path import random import fortune from tombot.registry import Command, get_easy_logger, Subscribe, BOT_START LOGGER = get_easy_logger('plugins.fortune') FORTUNE_FILES = [] SPECIALS = {} @Command('fortune', 'fortune') def fortune_cb(bot, *args, **kwargs): ''' Return a random quote from one of the quote files. ''' try: source = random.choice(FORTUNE_FILES) return fortune.get_random_fortune(source) except ValueError as ex: LOGGER.error('Fortune failed: %s', ex) return _('Be the quote you want to see on a wall.\n -- Error 20XX') @Subscribe(BOT_START) @Command('loadfortunes', 'fortune', hidden=True) def load_fortunes_cb(bot, message=None, *args, **kwargs): ''' (Re)load all fortune and specials files from their directories. '''
''' Provides commands to globally modify the bot's behaviour. ''' import logging import pydoc from .users_plugin import isadmin from tombot.registry import get_easy_logger, Command, Subscribe, BOT_START from tombot.registry import COMMAND_DICT, COMMAND_CATEGORIES from tombot.helper_functions import determine_sender, extract_query, reply_directly LOGGER = get_easy_logger('plugins.system') HELP_OVERVIEW = '' @Command('ping', 'system') def ping_cb(bot=None, message=None, *args, **kwargs): ''' Return 'pong' to indicate non-deadness. ''' return 'Pong' @Command('forcelog', 'system', hidden=True) def forcelog_cb(bot, message, *args, **kwargs): ''' Write a message to the root logger. ''' logging.info('Forcelog from %s: %s', message.getFrom(), message.getBody()) return @Command(['shutdown', 'halt'], 'system') def shutdown_cb(bot, message, *args, **kwargs): ''' Shut down the bot. ''' LOGGER.info('Stop message received from %s, content "%s"', message.getFrom(), message.getBody()) if not isadmin(bot, message):
time is compared to the time of the last seen message of the user. If the last message is equal to or greather than their timeout, a copy of the message is CC'd directly to the user. This is useful for 'productive' chats with many messages. ''' import re import datetime import operator from yowsup.layers.protocol_messages.protocolentities import TextMessageProtocolEntity from tombot.helper_functions import determine_sender, extract_query from tombot.registry import Command, Subscribe, get_easy_logger, BOT_MSG_RECEIVE from .users_plugin import jid_to_nick, nick_to_jid, nick_to_id, isadmin LOGGER = get_easy_logger('plugins.users.mentions') MENTION_PATTERN = r'(?<!\w)@\s?([^ .:,]+)[ .:,]?' MENTION_REGEX = re.compile(MENTION_PATTERN, re.IGNORECASE) @Subscribe(BOT_MSG_RECEIVE) def mention_handler_cb(bot, message, *args, **kwargs): ''' Scans message text for @mentions and notifies user if appropriate. ''' mentioned_sent = [] for nick in MENTION_REGEX.findall(message.getBody()): LOGGER.debug('Nick detected: %s', nick) # Who sent the message? senderjid = determine_sender(message)
last message is equal to or greather than their timeout, a copy of the message is CC'd directly to the user. This is useful for 'productive' chats with many messages. ''' import re import datetime import operator from yowsup.layers.protocol_messages.protocolentities import TextMessageProtocolEntity from tombot.helper_functions import determine_sender, extract_query from tombot.registry import Command, Subscribe, get_easy_logger, BOT_MSG_RECEIVE from .users_plugin import jid_to_nick, nick_to_jid, nick_to_id, isadmin LOGGER = get_easy_logger('plugins.users.mentions') MENTION_PATTERN = r'(?<!\w)@\s?([^ .:,]+)[ .:,]?' MENTION_REGEX = re.compile(MENTION_PATTERN, re.IGNORECASE) @Subscribe(BOT_MSG_RECEIVE) def mention_handler_cb(bot, message, *args, **kwargs): ''' Scans message text for @mentions and notifies user if appropriate. ''' mentioned_sent = [] for nick in MENTION_REGEX.findall(message.getBody()): LOGGER.debug('Nick detected: %s', nick) # Who sent the message? senderjid = determine_sender(message) LOGGER.debug('Resolving sender %s', senderjid)
''' Provides the plugin infrastructure and some helper functions for plugins. ''' import os.path import importlib from tombot.registry import get_easy_logger LOGGER = get_easy_logger('moduleloader') def load_plugins(): ''' Import all plugins. ''' root = os.path.dirname(__file__) LOGGER.info('Loading plugins from %s', root) for dummy, dummy, files in os.walk(root): for ffile in files: if ffile.endswith('_plugin.py'): modulename = ffile.strip('.py') LOGGER.info('Initializing plugin %s', modulename) try: importlib.import_module('.' + modulename, package=__name__) LOGGER.debug('%s loaded.', modulename) except (NameError, SyntaxError) as ex: LOGGER.error('Module %s cannot be loaded!', modulename) LOGGER.error(ex)
''' from __future__ import print_function from collections import namedtuple import datetime from datetime import date from dateutil.relativedelta import relativedelta import dateutil.rrule from dateutil.rrule import rrule from apscheduler.jobstores.base import JobLookupError import tombot.rpc from tombot.registry import Command, get_easy_logger, Subscribe, BOT_START, BOT_SHUTDOWN LOGGER = get_easy_logger('plugins.doekoe') Rule = namedtuple('rule', 'name rule relocator') def doekoe_neo(relative_to=datetime.datetime.today()): ''' Bereken wanneer de uitbetalingen in RULES gebeuren. Vraag uw specialist en/of gebruik uw ogen om de inhoud van RULES te achterhalen. ''' result = '' relative_to.replace(hour=0, minute=0, second=0, microsecond=0) LOGGER.debug('relative_to %s', relative_to) for item in next_occurrences(relative_to): LOGGER.debug('%s %s', item[0], item[1])
''' ABAS: Automated Birthday Announcement System ''' from apscheduler.jobstores.base import JobLookupError from tombot.registry import get_easy_logger, Subscribe, BOT_START, BOT_SHUTDOWN from tombot.rpc import remote_send LOGGER = get_easy_logger('plugins.abas') def announce_bday(name, recipient): ''' Send a congratulation for name to recipient. ''' LOGGER.info('Congratulating %s', name) body = 'Gefeliciteerd, {}!'.format(name) remote_send(body, recipient) @Subscribe(BOT_START) def abas_register_cb(bot, *args, **kwargs): ''' Add jobs to the scheduler for all birthdays. ''' LOGGER.info('Registering ABAs.') try: bot.cursor.execute('SELECT primary_nick,bday FROM users WHERE bday IS NOT NULL') except TypeError: LOGGER.error('Invalid date found, fix your database!') return results = bot.cursor.fetchall() for person in results: LOGGER.info('Scheduling ABA for %s', person[0]) bot.scheduler.add_job( announce_bday, 'cron', month=person[1].month, day=person[1].day,