Skip to content

Commit

Permalink
Reorganised code.
Browse files Browse the repository at this point in the history
  • Loading branch information
natfarleydev committed Jan 19, 2017
1 parent 72821f8 commit d7c1500
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 115 deletions.
5 changes: 4 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging
import importlib
import argparse
import pyconfig

import telepot
from telepot.aio.delegate import (per_chat_id,
Expand Down Expand Up @@ -96,7 +97,7 @@ def main(config):
all_cmds.add(cmd)

bot = telepot.aio.DelegatorBot(
BeardChatHandler.key,
pyconfig.get('key'),
list(delegator_beard_gen(Beard.beards))
)

Expand All @@ -123,6 +124,8 @@ def main(config):
level=parsed.loglevel)

# Set up the master beard
# TODO consider making this not a parrt of the BeardChatHandler class now
# that we use pyconfig.
BeardChatHandler.setup_beards(parsed.key, config.db_url)

# If the user does not specially request --no-help, set up help command.
Expand Down
43 changes: 43 additions & 0 deletions skybeard/bearddbtable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import logging
import dataset

import pyconfig

logger = logging.getLogger(__name__)


class BeardDBTable(object):
"""Placeholder for database object for beards.
For use with async with.
"""
def __init__(self, beard, table_name, **kwargs):
self.beard_name = type(beard).__name__
self.table_name = "{}_{}".format(
self.beard_name,
table_name
)
self.kwargs = kwargs

def __enter__(self):
self.db = dataset.connect(pyconfig.get('db_url'))
self.db.__enter__()
self.table = self.db.get_table(self.table_name, **self.kwargs)
logger.debug("BeardDBTable initalised with: self.table: {}, self.db: {}".format(
self.table, self.db))
return self

def __exit__(self, error_type, error_value, traceback):
self.db.__exit__(error_type, error_value, traceback)

del self.table
del self.db

def __getattr__(self, name):
"""If the normal getattr fails, try getattr(self.table, name)."""
try:
return getattr(self.table, name)
except AttributeError:
raise AttributeError(
"Open table not found. Are you using BeardDBTable with with?")
129 changes: 15 additions & 114 deletions skybeard/beards.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,23 @@
and http://stackoverflow.com/a/17401329
"""
import asyncio
import re
import logging
import json
import traceback
import dataset
from sqlalchemy.util import safe_reraise

import telepot.aio
import pyconfig

logger = logging.getLogger(__name__)


def regex_predicate(pattern):
"""Returns a predicate function which returns True if pattern is matched."""
def retfunc(chat_handler, msg):
try:
logging.debug("Matching regex: '{}' in '{}'".format(
pattern, msg['text']))
retmatch = re.match(pattern, msg['text'])
logging.debug("Match: {}".format(retmatch))
return retmatch
except KeyError:
return False

return retfunc


# TODO make command_predicate in terms of regex_predicate
def command_predicate(cmd):
"""Returns a predicate coroutine which returns True if command is sent."""
async def retcoro(beard_chat_handler, msg):
bot_username = await beard_chat_handler.get_username()
pattern = r"^/{}(?:@{}|[^@]|$)".format(
cmd,
bot_username,
)
try:
logging.debug("Matching regex: '{}' in '{}'".format(
pattern, msg['text']))
retmatch = re.match(pattern, msg['text'])
logging.debug("Match: {}".format(retmatch))
return retmatch
except KeyError:
return False
from .bearddbtable import BeardDBTable
from .logging import TelegramHandler
from .predicates import command_predicate

return retcoro
logger = logging.getLogger(__name__)


# TODO rename coro to coro_name or something better than that


class Command(object):
"""Holds information to determine whether a function should be triggered."""
def __init__(self, pred, coro, hlp=None):
Expand Down Expand Up @@ -82,57 +49,6 @@ def create_command(cmd_or_pred, coro, hlp=None):
raise TypeError("cmd_or_pred must be str or callable.")


class TelegramHandler(logging.Handler):
"""A logging handler that posts directly to telegram"""

def __init__(self, bot, parse_mode=None):
self.bot = bot
self.parse_mode = parse_mode
super().__init__()

def emit(self, record):
coro = self.bot.sender.sendMessage(
self.format(record), parse_mode=self.parse_mode)
asyncio.ensure_future(coro)


class BeardDBTable(object):
"""Placeholder for database object for beards.
For use with async with.
"""
def __init__(self, beard, table_name, **kwargs):
self.beard_name = type(beard).__name__
self.table_name = "{}_{}".format(
self.beard_name,
table_name
)
self.kwargs = kwargs

def __enter__(self):
self.db = dataset.connect(BeardChatHandler.db_url)
self.db.__enter__()
self.table = self.db.get_table(self.table_name, **self.kwargs)
logger.debug("BeardDBTable initalised with: self.table: {}, self.db: {}".format(
self.table, self.db))
return self

def __exit__(self, error_type, error_value, traceback):
self.db.__exit__(error_type, error_value, traceback)

del self.table
del self.db

def __getattr__(self, name):
"""If the normal getattr fails, try getattr(self.table, name)."""
try:
return getattr(self.table, name)
except AttributeError:
raise AttributeError(
"Open table not found. Are you using BeardDBTable with with?")


class Beard(type):
"""Metaclass for creating beards."""

Expand Down Expand Up @@ -166,29 +82,6 @@ def register(cls, beard):
cls.beards.append(beard)


class Filters:
"""Filters used to call plugin methods when particular types of
messages are received.
For usage, see description of the BeardChatHandler.__commands__ variable.
"""
@classmethod
def text(cls, chat_handler, msg):
"""Filters for text messages"""
return "text" in msg

@classmethod
def document(cls, chat_handler, msg):
"""Filters for sent documents"""
return "document" in msg

@classmethod
def location(cls, chat_handler, msg):
"""Filters for sent locations"""
return "location" in msg


class ThatsNotMineException(Exception):
"""Raised if data does not match beard.
Expand Down Expand Up @@ -257,7 +150,13 @@ def __init__(self, *args, **kwargs):
self._handler = TelegramHandler(self)
self.logger.addHandler(self._handler)

def get_db_table(self, table_name, **kwargs):
def create_db_table(self, table_name, **kwargs):
"""Creates `BeardDBTable` using current beard.
Note that this does not create a table in the database. Tables are only
created once the `BeardDBTable` is used with `with`.
"""
return BeardDBTable(self, table_name, **kwargs)

def on_close(self, e):
Expand Down Expand Up @@ -306,6 +205,8 @@ def deserialize(self, data):
@classmethod
def setup_beards(cls, key, db_url):
"""Perform setup necessary for all beards."""
pyconfig.set('key', key)
pyconfig.set('db_url', db_url)
cls.key = key
cls.db_url = db_url

Expand Down
16 changes: 16 additions & 0 deletions skybeard/logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import asyncio
import logging


class TelegramHandler(logging.Handler):
"""A logging handler that posts directly to telegram"""

def __init__(self, bot, parse_mode=None):
self.bot = bot
self.parse_mode = parse_mode
super().__init__()

def emit(self, record):
coro = self.bot.sender.sendMessage(
self.format(record), parse_mode=self.parse_mode)
asyncio.ensure_future(coro)
63 changes: 63 additions & 0 deletions skybeard/predicates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import logging
import re

logger = logging.getLogger(__name__)


def regex_predicate(pattern):
"""Returns a predicate function which returns True if pattern is matched."""
def retfunc(chat_handler, msg):
try:
logging.debug("Matching regex: '{}' in '{}'".format(
pattern, msg['text']))
retmatch = re.match(pattern, msg['text'])
logging.debug("Match: {}".format(retmatch))
return retmatch
except KeyError:
return False

return retfunc


# TODO make command_predicate in terms of regex_predicate
def command_predicate(cmd):
"""Returns a predicate coroutine which returns True if command is sent."""
async def retcoro(beard_chat_handler, msg):
bot_username = await beard_chat_handler.get_username()
pattern = r"^/{}(?:@{}|[^@]|$)".format(
cmd,
bot_username,
)
try:
logging.debug("Matching regex: '{}' in '{}'".format(
pattern, msg['text']))
retmatch = re.match(pattern, msg['text'])
logging.debug("Match: {}".format(retmatch))
return retmatch
except KeyError:
return False

return retcoro


class Filters:
"""Filters used to call plugin methods when particular types of
messages are received.
For usage, see description of the BeardChatHandler.__commands__ variable.
"""
@classmethod
def text(cls, chat_handler, msg):
"""Filters for text messages"""
return "text" in msg

@classmethod
def document(cls, chat_handler, msg):
"""Filters for sent documents"""
return "document" in msg

@classmethod
def location(cls, chat_handler, msg):
"""Filters for sent locations"""
return "location" in msg

0 comments on commit d7c1500

Please sign in to comment.