Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ var/
.installed.cfg
*.egg

# IDE
.vscode/

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
Expand Down
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,4 @@ lxml = "*"
prompt_toolkit = "*"
tweepy = "*"
meinheld = "*"
python-telegram-bot = "*"
560 changes: 333 additions & 227 deletions Pipfile.lock

Large diffs are not rendered by default.

141 changes: 141 additions & 0 deletions pyborg/pyborg/mod/mod_telegram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import logging
from functools import partial

import toml
import pyborg.pyborg
import pyborg.commands
import requests

from telegram.ext import Updater, CommandHandler, MessageHandler, Filters

logger = logging.getLogger(__name__)

class Registry(object):

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actively dropping python 2 support so this can be the py3 native declaration.

"""Command registry of decorated pyborg commands"""

def __init__(self, mod_irc):
self.registered = {}
self.mod_irc = mod_irc

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be generic pending #93 or mod specific to avoid confusion in traceback/debugging.


def add(self, name, ob, internals, pass_msg):
if internals:
self.registered[name] = partial(ob, self.mod_irc.settings["multiplex"], multi_server="http://localhost:2001/")
else:
self.registered[name] = ob


class ModTelegram:
def __init__(self, toml_file="telegram.toml"):
self.settings = toml.load(toml_file)
try:
self.multiplexing = self.settings['pyborg']['multiplex']
self.multi_server = self.settings['pyborg']['multiplex_server']
self.multi_port = self.settings['pyborg']['multiplex_port']
except KeyError:
logger.info("Missing config key, you get defaults.")

self.reply_rate = 100

if not self.multiplexing:
self.pyborg = pyborg.pyborg.pyborg()
else:
self.serverUrl = "http://{}:{}".format(self.multi_server, self.multi_port)
self.pyborg = None

# Telegram Commands setup
self.registry = Registry(self)

def start(self):
"""Start the bot."""
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater(self.settings['telegram']['token'], use_context=True)

# Get the dispatcher to register handlers
dp = updater.dispatcher

# on different commands - answer in Telegram
# dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", self.on_help))
dp.add_handler(CommandHandler("reply_rate", self.on_admin_command))
dp.add_handler(CommandHandler("info", self.on_admin_command))
dp.add_handler(CommandHandler("known", self.on_admin_command))

# on noncommand i.e message - echo the message on Telegram
dp.add_handler(MessageHandler(Filters.text, self.on_echo))

# log all errors
dp.add_error_handler(self.on_error)

# Start the Bot
updater.start_polling()

# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()

def on_error(self, update: Updater, context) -> None:
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)

def on_admin_command(self, update: Updater, context) -> None:
message = update.message.text
if update.message.from_user.username in self.settings['telegram']['admins']:
parts = message.split(' ')

if parts[0] == '/reply_rate' and len(parts) > 1:
self.reply_rate = int(parts[1])
update.message.reply_text("New rate: {}".format(self.reply_rate))
if parts[0] == '/info':
resp = requests.get("{}/info".format(self.serverUrl))
update.message.reply_text(resp.text)
if parts[0] == '/known' and len(parts) > 1:
word = parts[1]
resp = requests.get("{}/known?word={}".format(self.serverUrl, word))
update.message.reply_text(resp.text)
# if parts[0] == '/replace' and len(parts) > 2:
# self.pyborg.replace(parts[1], parts[2])

def on_help(self, update: Updater, context) -> None:
"""Send a message when the command /help is issued."""
update.message.reply_text('Commands: `/reply_rate`, `/info`, `/known`.')

def on_echo(self, update: Updater, context) -> None:
"""Echo the user message."""
body = update.message.text

username = update.message.from_user.first_name

diff = 0
for entity in update.message.entities:
offset = entity.offset - diff
length = entity.length
diff = length - 5
body = body[0:offset] + "#nick" + body[(offset+length):]

if self.multiplexing:
d = {"body": body, "reply_rate": self.reply_rate, "learning": 1, "owner": 1}
resp = requests.post("{}/process".format(self.serverUrl), data=d)

if resp.status_code == requests.codes.ok:
if resp.text:
reply = resp.text.replace("#nick", username)
# update.message.reply_text(reply)
context.bot.send_message(
chat_id=update.message.chat.id,
text=reply
)
else:
logger.error(resp)
else:
resp = self.pyborg.reply(body)
reply = resp.text.replace("#nick", username)
context.bot.send_message(
chat_id=update.message.chat.id,
text=reply
)

def teardown(self) -> None:
pass
16 changes: 16 additions & 0 deletions pyborg/pyborg_entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from pyborg.mod.mod_http import bottle
from pyborg.mod.mod_irc import ModIRC
from pyborg.mod.mod_linein import ModLineIn
from pyborg.mod.mod_telegram import ModTelegram
from pyborg.mod.mod_reddit import PyborgReddit
from pyborg.util.bottle_plugin import BottledPyborg
from pyborg.util.util_cli import mk_folder
Expand Down Expand Up @@ -456,6 +457,21 @@ def reddit(conf_file):
raise


@cli_base.command()
@click.option("--conf-file", default="telegram.toml")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs to be resolved from the folder global.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so like Path(folder, "conf", "telegram.toml")?

def telegram(conf_file):
"Runs the teelgram module"
bot = ModTelegram(conf_file)
try:
bot.start()
except KeyboardInterrupt:
bot.teardown()
sys.exit()
except Exception:
bot.teardown()
raise


@cli_base.command()
@click.option("--multiplex", default=True, type=click.BOOL)
def linein(multiplex):
Expand Down
2 changes: 1 addition & 1 deletion pyborg/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
scripts=["pyborg_entrypoint.py"],
author="Jack Laxson",
author_email="jackjrabbit@gmail.com",
description="Markov chain bot for many protocols (discord, irc, twitter, mastodon, file, linein) which generates replies to messages",
description="Markov chain bot for many protocols (discord, irc, telegram, twitter, mastodon, file, linein) which generates replies to messages",
long_description=long_description,
long_description_content_type='text/markdown',
license="GPL v3 or later",
Expand Down
8 changes: 8 additions & 0 deletions pyborg/telegram.example.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[telegram]
token = "FAKETOKEN"
admins = ["yourpseudo"]

[pyborg]
multiplex = true
multiplex_server = "localhost"
multiplex_port = 2001