1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-11-23 19:44:20 +00:00

First commit!

This commit is contained in:
Steffo 2019-03-15 01:05:30 +01:00
commit 06e79562f8
11 changed files with 125 additions and 0 deletions

1
requirements.txt Normal file
View file

@ -0,0 +1 @@
python-telegram-bot>=11.1.0

0
royalnet/__init__.py Normal file
View file

View file

61
royalnet/bots/telegram.py Normal file
View file

@ -0,0 +1,61 @@
import telegram
import asyncio
import typing
from ..commands import PingCommand
from ..utils import asyncify, Call
class TelegramBot:
def __init__(self, api_key: str):
self.bot = telegram.Bot(api_key)
self.should_run = False
self.offset = -100
self.commands = {
"/ping": PingCommand
}
class TelegramCall(Call):
interface_name = "telegram"
interface_obj = self
async def reply(call, text: str):
await asyncify(call.channel.send_message, text, parse_mode="HTML")
self.Call = TelegramCall
async def run(self):
self.should_run = True
while self.should_run:
# Get the latest 100 updates
last_updates: typing.List[telegram.Update] = await asyncify(self.bot.get_updates, offset=self.offset)
# Handle updates
for update in last_updates:
# noinspection PyAsyncCall
asyncio.create_task(self.handle_update(update))
# Recalculate offset
try:
self.offset = last_updates[-1].update_id + 1
except IndexError:
pass
# Wait for a while TODO: use long polling
await asyncio.sleep(1)
async def handle_update(self, update: telegram.Update):
# Skip non-message updates
if update.message is None:
return
message: telegram.Message = update.message
# Skip no-text messages
if message.text is None:
return
text: str = message.text
# Find and clean parameters
command_text, *parameters = text.split(" ")
command_text.replace(f"@{self.bot.username}", "")
# Find the function
try:
command = self.commands[command_text]
except KeyError:
# Skip inexistent commands
return
# Call the command
return await self.Call(message.chat, command).run()

View file

@ -0,0 +1,3 @@
from .ping import PingCommand
__all__ = ["PingCommand"]

View file

@ -0,0 +1,6 @@
from ..utils import Command, Call
class PingCommand(Command):
async def common(self, call: Call, *args, **kwargs):
await call.reply("Pong!")

View file

@ -0,0 +1,5 @@
from .asyncify import asyncify
from .call import Call
from .command import Command
__all__ = ["asyncify", "Call", "Command"]

View file

@ -0,0 +1,8 @@
import asyncio
import functools
import typing
async def asyncify(function: typing.Callable, *args, **kwargs):
loop = asyncio.get_running_loop()
return await loop.run_in_executor(None, functools.partial(function, *args, **kwargs))

25
royalnet/utils/call.py Normal file
View file

@ -0,0 +1,25 @@
from .command import Command
class Call:
"""A command call. Still an abstract class, subbots should create a new call from this."""
# These parameters / methods should be overridden
interface_name = NotImplemented
interface_obj = NotImplemented
async def reply(cls, text: str):
"""Send a text message to the channel the call was made."""
raise NotImplementedError()
# These parameters / methods should be left alone
def __init__(self, channel, command: Command):
self.channel = channel
self.command = command
async def run(self, *args, **kwargs):
try:
coroutine = getattr(self.command, self.interface_name)
except AttributeError:
coroutine = getattr(self.command, "common")
return await coroutine(self.command, self, *args, **kwargs)

10
royalnet/utils/command.py Normal file
View file

@ -0,0 +1,10 @@
import typing
if typing.TYPE_CHECKING:
from .call import Call
class Command:
"""A generic command, called from any source."""
async def common(self, call: "Call", *args, **kwargs):
raise NotImplementedError()

6
test.py Normal file
View file

@ -0,0 +1,6 @@
import asyncio
from royalnet.bots.telegram import TelegramBot
bot = TelegramBot("375232708:AAExsD_prmxJOXzmJwYZyNUt5zc_EbXxR38")
asyncio.get_event_loop().run_until_complete(bot.run())