1
Fork 0
mirror of https://github.com/Steffo99/greed.git synced 2024-11-25 23:24:19 +00:00

Continue work on the basic bot code

This commit is contained in:
Steffo 2017-12-12 19:56:38 +01:00
parent d3f420cf6e
commit cb9dbb1305
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS.
GPG key ID: C27544372FBB445D
3 changed files with 160 additions and 107 deletions

25
core.py
View file

@ -2,11 +2,13 @@ import os
import sys import sys
import configparser import configparser
import telegram import telegram
import threading
import time import time
import strings import strings
import worker import worker
def main():
"""The core code of the program. Should be run only in the main process!"""
# Check if a configuration file exists, create one if it doesn't and get the template version number. # Check if a configuration file exists, create one if it doesn't and get the template version number.
with open("config/template_config.ini") as template_file: with open("config/template_config.ini") as template_file:
# Check if the config file exists # Check if the config file exists
@ -61,10 +63,10 @@ chat_workers = {}
# Current update offset; if None it will get the last 100 unparsed messages # Current update offset; if None it will get the last 100 unparsed messages
next_update = None next_update = None
# Main loop of the program # Main loop of the program
while True: while True:
# Get a new batch of 100 updates and mark the last 100 parsed as read # Get a new batch of 100 updates and mark the last 100 parsed as read
# TODO: handle possible errors
updates = bot.get_updates(offset=next_update) updates = bot.get_updates(offset=next_update)
# Parse all the updates # Parse all the updates
for update in updates: for update in updates:
@ -97,19 +99,30 @@ while True:
if receiving_worker is None: if receiving_worker is None:
# Suggest that the user restarts the chat with /start # Suggest that the user restarts the chat with /start
bot.send_message(update.message.chat.id, strings.error_no_worker_for_chat) bot.send_message(update.message.chat.id, strings.error_no_worker_for_chat)
# Skip the update
continue
# Forward the update to the worker # Forward the update to the worker
receiving_worker.pipe.send(update) receiving_worker.pipe.send(update)
# If the update is a inline keyboard press... # If the update is a inline keyboard press...
if update.inline_query is not None: if update.inline_query is not None:
# Forward the update to the corresponding worker # Forward the update to the corresponding worker
receiving_worker = chat_workers.get(update.message.chat.id) receiving_worker = chat_workers.get(update.inline_query.chat.id)
# Ensure a worker exists for the chat # Ensure a worker exists for the chat
if receiving_worker is None: if receiving_worker is None:
# Suggest that the user restarts the chat with /start # Suggest that the user restarts the chat with /start
bot.send_message(update.message.chat.id, strings.error_no_worker_for_chat) bot.send_message(update.inline_query.chat.id, strings.error_no_worker_for_chat)
# Skip the update
continue
# Forward the update to the worker # Forward the update to the worker
receiving_worker.pipe.send(update) receiving_worker.pipe.send(update)
# Mark the update as read by increasing the update_offset # If there were any updates...
next_update = update.update_id + 1 if len(updates):
# Mark them as read by increasing the update_offset
next_update = updates[-1].update_id + 1
# Temporarily prevent rate limits (remove this later) # Temporarily prevent rate limits (remove this later)
time.sleep(5) time.sleep(5)
# Run the main function only in the main process
if __name__ == "__main__":
main()

View file

@ -1,5 +1,10 @@
# Strings / localization file for greed # Strings / localization file for greed
# Can be edited, but DON'T REMOVE THE REPLACEMENT FIELDS (words surrounded by {curly braces}) # Can be edited, but DON'T REMOVE THE REPLACEMENT FIELDS (words surrounded by {curly braces})
# TODO: maybe add a preformat to all strings in this file
# Answer: the start command was sent and the bot should welcome the user
conversation_after_start = "Ciao!\n" \
"Benvenuto su greed!"
# Error: message received not in a private chat # Error: message received not in a private chat
error_nonprivate_chat = "⚠️ Questo bot funziona solo in chat private." error_nonprivate_chat = "⚠️ Questo bot funziona solo in chat private."

View file

@ -1,12 +1,20 @@
import multiprocessing import multiprocessing
import telegram import telegram
import strings
class StopSignal:
"""A data class that should be sent to the worker when the conversation has to be stopped abnormally."""
def __init__(self, reason: str=""):
self.reason = reason
class ChatWorker: class ChatWorker:
"""A worker for a single conversation. A new one should be created every time the /start command is sent.""" """A worker for a single conversation. A new one should be created every time the /start command is sent."""
def __init__(self, bot: telegram.Bot, chat: telegram.Chat): def __init__(self, bot: telegram.Bot, chat: telegram.Chat):
# A pipe connecting the main process to the chat process is created # A pipe connecting the main process to the chat process is created
in_pipe, out_pipe = multiprocessing.Pipe(duplex=False) out_pipe, in_pipe = multiprocessing.Pipe(duplex=False)
# The sending pipe is stored in the ChatWorker class, allowing the forwarding of messages to the chat process # The sending pipe is stored in the ChatWorker class, allowing the forwarding of messages to the chat process
self.pipe = in_pipe self.pipe = in_pipe
# A new process running the conversation handler is created, and the receiving pipe is passed to its arguments to enable the receiving of messages # A new process running the conversation handler is created, and the receiving pipe is passed to its arguments to enable the receiving of messages
@ -16,13 +24,40 @@ class ChatWorker:
"""Start the worker process.""" """Start the worker process."""
self.process.start() self.process.start()
def stop(self): def stop(self, reason: str=""):
# Gracefully stop the worker process """Gracefully stop the worker process"""
# TODO: send a stop message to the process # Send a stop message to the process
raise NotImplementedError() self.pipe.send(StopSignal(reason))
# Wait for the process to stop # Wait for the process to stop
self.process.join() self.process.join()
def conversation_handler(bot: telegram.Bot, chat: telegram.Chat, pipe: multiprocessing.Connection): def receive_next_update(pipe) -> telegram.Update:
"""Get the next update from a pipe.
If no update is found, block the process until one is received.
If a stop signal is sent, try to gracefully stop the process."""
# Receive data from the pipe
data = pipe.recv()
# Check if the data is a stop signal instance
if isinstance(data, StopSignal):
# Gracefully stop the process
graceful_stop()
# Return the received update
return data
def graceful_stop():
"""Handle the graceful stop of the process."""
raise NotImplementedError() raise NotImplementedError()
def conversation_handler(bot: telegram.Bot, chat: telegram.Chat, pipe):
"""This function is ran once for every conversation (/start command) by a separate process."""
# TODO: catch all the possible exceptions
# Welcome the user to the bot
bot.send_message(chat.id, strings.conversation_after_start)
# TODO: Send a command list or something
while True:
# For now, echo the sent message
update = receive_next_update(pipe)
bot.send_message(chat.id, update.message.text)