def proxy_telegram(api_key, https=False, host="localhost", hookpath="/income/{API_KEY}", full_url=None): logger.debug( "https: {!r}, host: {!r}, hookpath: {!r}, full_url: {!r}".format( https, host, hookpath, full_url)) if full_url is None: full_url = "http" + ("s" if https else "") + "://" + host + hookpath.format( API_KEY=api_key) # end if bot = Bot(api_key, return_python_objects=False) if bot.get_webhook_info()["result"]["url"] == "": logger.info("Webhook unset correctly. No need to change.") else: logger.debug(bot.delete_webhook()) # end def last_update = 0 while True: result = bot.get_updates(offset=last_update, poll_timeout=1000) updates = result["result"] n = len(updates) for i, update in enumerate(updates): last_update = update['update_id'] + 1 logger.debug( "Polling update ({i:03}/{n:03}|{l}):\n{u}\n{r!r}".format( r=update, i=i, n=n, l=last_update, u=full_url)) requests.post(full_url, json=update, headers={ 'Content-Type': 'application/json', 'Cache-Control': 'no-cache' })
def __init__(self, API_KEY, debug=False): if API_KEY is None: API_KEY = self.ask_for_apikey() self._api_key = API_KEY self.bot = Bot(API_KEY, return_python_objects=True) self.me = self.bot.get_me() logger.info("Information about myself: {info}".format(info=self.me)) self.METHOD_INCLUDES.update({ "debug": self.cmd_debug, "help": self.cmd_help, }) self.color = Color() self.update_thread = self.create_update_thread() self.functions = {k: v for k, v in self.get_functions()} self.current_candidates = [] self.is_debug = debug self.query = "pytgbot> " # in front of the input. self.register_tab_completion()
def start_multimon(freq, prot, minlen, tid, rid): # replace POCSAG512 with -a POCSAG512 prot = prot.replace(" ", " -a ") # create telegram bot bot = Bot(tid) # create deque d = collections.deque(maxlen=100) # call multimon call = "rtl_fm -d0 -f " + freq + " -s 22050 | multimon-ng -t raw -a " + prot + " -f alpha -t raw /dev/stdin -" print(call) mm = subprocess.Popen(call, shell=True, stdout=subprocess.PIPE) while mm.poll() is None: # process new pocsag entry output = mm.stdout.readline().decode('utf-8') # read new line print(output) if "Alpha" not in output: # check if non alpha element continue # if yes, continue output = output.replace("<NUL>", "") # replace terminator sequence if output in d: # if the message is already in the list -> skip (pager messages might be sent multiple times) continue d.append(output) # add to deque msg = output.split("Alpha:", 1)[1] # get the message if int(len(msg)) < int(minlen): # if msg length < minlen skip continue time = strftime("%Y-%m-%d %H:%M", gmtime()) # get timestamp print(msg) # print the message try: bot.send_message(rid, 'Time: ' + time + '\nMessage: ' + msg) # send it. except: pass
class BBBot: def __init__(self, config: Config): """Create a bot using the config. Required keys: bot.key, bot.running, bot.chat_id, bot.offset""" self.config = config self.running = config['bot'].getboolean('running', True) self._bot = Bot(config['bot'].get('key')) self._chat_id = config['bot'].getint('chat_id', 0) self.consume_messages() def consume_messages(self): offset = self.config['bot'].getint('offset', 0) for msg in self._bot.do('getUpdates', offset=offset, request_timeout=100): if 'channel_post' in msg and msg['channel_post']['chat']['id'] == self._chat_id \ and 'text' in msg['channel_post']: text = msg['channel_post']['text'].lower() if text in PAUSE_COMMANDS: self.running = False elif text in START_COMMANDS: self.running = True offset = msg['update_id'] self.config.set('bot', 'running', str(self.running)) self.config.set('bot', 'offset', str(offset)) self.config.persist() def send_message(self, message: str, formatting='HTML'): self._bot.send_message(self._chat_id, message, parse_mode=formatting)
def main(): # get you bot instance. bot = Bot(API_KEY, return_python_objects=False) # Set `return_python_objects=False` # because we need to be really fast to answer inline queries in time, # and my computer is crap, # so any nanosecond this adds is too much, # resulting in the queries timing out. logging.add_colored_handler(logger_name=__name__, level=logging.DEBUG) my_info = bot.get_me() logger.info("Information about myself: {info}".format(info=my_info)) last_update_id = 0 mlfw = MLFW(bot) while True: # loop forever. for update in bot.get_updates(limit=100, offset=last_update_id+1, error_as_empty=True).result: last_update_id = update.update_id logger.debug(update) if "inline_query" not in update or not update.inline_query: continue inline_query_id = update.inline_query.id query = update.inline_query.query query_offset = update.inline_query.offset mlfw.search(query, inline_query_id, offset=query_offset)
def send(self, sender: PytgbotApiBot) -> PytgbotApiMessage: self.prepare_file() assert isinstance(self.file, (InputFile, InputFileFromDisk, InputFileFromURL, str)) if not self.file_id and not any([self.file.file_name.endswith(x) for x in [".jpg", ".jpeg", ".gif", ".png", ".tif", ".bmp"]]): # set the suffix if self.file.file_mime in ["image/jpg", "image/jpeg", "image/jpe"]: # manually, to avoid .jpe ending. self.file.file_name += ".jpg" else: import mimetypes ext = mimetypes.guess_extension(self.file.file_mime) # automatically if ext not in [".jpg", ".jpeg", ".gif", ".png", ".tif", ".bmp"]: ext = ".unknown-file-type.png" # At least we can try setting it as .png self.file.file_name += ext # end if # end if try: return sender.send_photo( chat_id=self.receiver, photo=self.file, caption=self.caption, reply_to_message_id=self.reply_id, reply_markup=self.reply_markup, disable_notification = self.disable_notification ) except TgApiServerException as e: if e.error_code == 400 and e.description.startswith('bad request') and 'reply message not found' in e.description: logger.debug('Trying to resend without reply_to.') return sender.send_photo( chat_id=self.receiver, photo=self.file, caption=self.caption, reply_to_message_id=self.reply_id, reply_markup=self.reply_markup, disable_notification=self.disable_notification ) # end if raise # else it just raises as usual
class LiveTestCase(asynctest.TestCase): def setUp(self) -> None: self.api_bot = Bot(TEST_BOT_AUTH) self.user_bot = Bot(TEST_USER_AUTH) # end def async def test_send_location(self): self.user_bot.send_message()
def __init__(self, config: Config): """Create a bot using the config. Required keys: bot.key, bot.running, bot.chat_id, bot.offset""" self.config = config self.running = config['bot'].getboolean('running', True) self._bot = Bot(config['bot'].get('key')) self._chat_id = config['bot'].getint('chat_id', 0) self.consume_messages()
def send(self, sender: PytgbotApiBot, receiver, reply_id)->PytgbotApiMessage: if self.receiver: receiver = self.receiver # end if if self.reply_id is not DEFAULT_MESSAGE_ID: reply_id = self.reply_id # end if self.prepare_file() assert isinstance(self.file, (InputFile, InputFileFromDisk, InputFileFromURL)) if not any([self.file.file_name.endswith(x) for x in [".jpg", ".jpeg", ".gif", ".png", ".tif", ".bmp"]]): if self.file.file_mime in ["image/jpg", "image/jpeg", "image/jpe"]: # manually, to avoid .jpe ending. self.file.file_name+=".jpg" else: import mimetypes ext = mimetypes.guess_extension(self.file.file_mime) # automatically if ext not in [".jpg", ".jpeg", ".gif", ".png", ".tif", ".bmp"]: ext = ".unknown-file-type.png" # At least we can try setting it as .png self.file.file_name += ext try: return sender.send_photo( receiver, self.file, caption=self.caption, reply_to_message_id=reply_id, reply_markup=self.reply_markup, disable_notification = self.disable_notification ) except TgApiServerException as e: should_backoff(e) # checks if it should raise an DoRetryException raise # else it just raises as usual
def actual_sending(self, sender: PytgbotApiBot, receiver, reply_id): return sender.send_sticker( receiver, self.file_id, reply_to_message_id=reply_id, reply_markup=self.reply_markup, disable_notification=self.disable_notification)
def actual_sending(self, sender: PytgbotApiBot, ignore_reply: bool = False): return sender.send_audio( chat_id=self.receiver, audio=self.file_id, reply_to_message_id=self.reply_id if not ignore_reply else None, caption=self.caption, parse_mode=self.parse_mode, reply_markup=self.reply_markup, disable_notification=self.disable_notification )
def main(): logging.add_colored_handler(level=logging.DEBUG) # get you bot instance. bot = Bot(API_KEY, return_python_objects=True) print(bot.get_me()) # do the update loop. last_update_id = 0 while True: # loop forever. updates = bot.get_updates(limit=100, offset=last_update_id + 1) for update in updates: # for every new update last_update_id = update.update_id print(update) result = update.to_array() assert isinstance(result, dict) print(result)
def send(self, sender: PytgbotApiBot) -> PytgbotApiMessage: self.prepare_file() assert isinstance( self.file, (InputFile, InputFileFromDisk, InputFileFromURL, str)) if not self.file_id and not any([ self.file.file_name.endswith(x) for x in [".jpg", ".jpeg", ".gif", ".png", ".tif", ".bmp"] ]): # set the suffix if self.file.file_mime in ["image/jpg", "image/jpeg", "image/jpe" ]: # manually, to avoid .jpe ending. self.file.file_name += ".jpg" else: import mimetypes ext = mimetypes.guess_extension( self.file.file_mime) # automatically if ext not in [ ".jpg", ".jpeg", ".gif", ".png", ".tif", ".bmp" ]: ext = ".unknown-file-type.png" # At least we can try setting it as .png self.file.file_name += ext # end if # end if try: return sender.send_photo( chat_id=self.receiver, photo=self.file, caption=self.caption, reply_to_message_id=self.reply_id, reply_markup=self.reply_markup, disable_notification=self.disable_notification) except TgApiServerException as e: should_backoff(e) # checks if it should raise an DoRetryException raise # else it just raises as usual
def send(self, sender: PytgbotApiBot, receiver, reply_id) -> PytgbotApiMessage: if self.receiver: receiver = self.receiver # end if if self.reply_id is not DEFAULT_MESSAGE_ID: reply_id = self.reply_id # end if try: result = sender.send_message( receiver, self.text, parse_mode=self.parse_mode, disable_notification=self.disable_notification, reply_to_message_id=reply_id, reply_markup=self.reply_markup, disable_web_page_preview=self.disable_web_page_preview) except TgApiServerException as e: should_backoff(e) # checks if it should raise an DoRetryException raise # else it just raises as usual # end try # if result and self._next_msg: # # pass current message as reply id. # self._next_msg.reply_id == result # # end if return result
def send(self, sender: PytgbotApiBot) -> PytgbotApiMessage: try: return sender.forward_message( chat_id=self.receiver, from_chat_id=self.from_chat_id, message_id=self.msg_id, disable_notification=self.disable_notification ) except TgApiServerException as e: raise # else it just raises as usual
def send(self, sender: PytgbotApiBot) -> PytgbotApiMessage: """ :rtype: PytgbotApiMessage """ return sender.send_media_group( chat_id=self.receiver, media=self.media, disable_notification=self.disable_notification, reply_to_message_id=self.reply_id )
def actual_sending(self, sender: PytgbotApiBot, receiver, reply_id): return sender.send_audio( chat_id=receiver, audio=self.file_id, reply_to_message_id=reply_id, caption=self.caption, parse_mode=self.parse_mode, reply_markup=self.reply_markup, disable_notification=self.disable_notification)
def actual_sending(self, sender: PytgbotApiBot) -> PytgbotApiMessage: assert isinstance(self.reply_id, int) # not DEFAULT_MESSAGE_ID return sender.send_document( chat_id=self.receiver, document=self.file, caption=self.caption, parse_mode=self.parse_mode, reply_to_message_id=self.reply_id, reply_markup=self.reply_markup, disable_notification=self.disable_notification)
def send(self, sender: PytgbotApiBot) -> PytgbotApiMessage: try: return sender.forward_message( chat_id=self.receiver, from_chat_id=self.from_chat_id, message_id=self.msg_id, disable_notification=self.disable_notification) except TgApiServerException as e: should_backoff(e) # checks if it should raise an DoRetryException raise # else it just raises as usual
def actual_sending(self, sender: PytgbotApiBot, ignore_reply: bool = False) -> PytgbotApiMessage: assert_type_or_raise(self.reply_id, int, None, parameter_name="self.reply_id") # not DEFAULT_MESSAGE_ID return sender.send_document( chat_id=self.receiver, document=self.file, caption=self.caption, parse_mode=self.parse_mode, reply_to_message_id=self.reply_id if not ignore_reply else None, reply_markup=self.reply_markup, disable_notification=self.disable_notification, allow_sending_without_reply=True, )
def main(API_KEY): if API_KEY is None: API_KEY = answer("Input API key") # get your bot instance. bot = Bot(API_KEY, return_python_objects=True) my_info=bot.get_me() print("Information about myself: {info}".format(info=my_info)) in_tread = Thread(target=get_updates, name="cli input thread", args=(bot,)) in_tread.daemon = True in_tread.start() notice = "You can enter commands now.\n" while True: cmd = input(notice) notice = "" # never display again. try: result_str = parse_input(bot, cmd) if result_str: print(result_str) except Exception as e: logger.exception("Error.") print("Error: " + str(e))
def init_bot(self): """ Creates the bot, and retrieves information about the bot itself (username, user_id) from telegram. :return: """ if not self._bot: # so you can manually set it before calling `init_app(...)`, # e.g. a mocking bot class for unit tests self._bot = Bot(self._api_key, return_python_objects=self._return_python_objects) elif self._bot.return_python_objects != self._return_python_objects: # we don't have the same setting as the given one raise ValueError("The already set bot has return_python_objects {given}, but we have {our}".format( given=self._bot.return_python_objects, our=self._return_python_objects )) # end def myself = self._bot.get_me() if self._bot.return_python_objects: self._user_id = myself.id self._username = myself.username else: self._user_id = myself["result"]["id"] self._username = myself["result"]["username"]
def main(API_KEY): if API_KEY is None: API_KEY = answer("Input API key") # get your bot instance. bot = Bot(API_KEY, return_python_objects=True) my_info = bot.get_me() print("Information about myself: {info}".format(info=my_info)) in_tread = Thread(target=get_updates, name="cli input thread", args=(bot, )) in_tread.daemon = True in_tread.start() notice = "You can enter commands now.\n" while True: cmd = input(notice) notice = "" # never display again. try: result_str = parse_input(bot, cmd) if result_str: print(result_str) except Exception as e: logger.exception("Error.") print("Error: " + str(e))
def retrieve_and_format_user(bot: Bot, user_id: Union[str, int], do_link: bool = False, prefer_username: bool = False, id_fallback: bool = False, user_tag: bool = None, id_tag: bool = None, html_escape: bool = True) -> str: """ Gets a user's name by given user id. If you want to use it in html you should probably keep html character escaping enabled (`html_escape=True`). You can specify to wrap the user name string or id string in html tags, with the user_tag or id_tag arguments. If the name is "Max Mustermann", with `user_tag="b"` it will return "<b>Max Mustermann</b>". Could return `None` if that user was either not found, or had no usable data. :param bot: The bot instance to use to check the user. :param user_id: the id of the telegram user. :param do_link: If it should do a link instead of bold or anything provided in `user_tag` or `id_tag`. :param prefer_username: if you should prefer the @username over the real name :param id_fallback: if no real name and no @username exists, if it should fallback to the user id: user#123456 :param user_tag: Name of the html tag to wrap the user string in (not the @username) :param id_tag: Name of the html tag to wrap the user id in. (`id_fallback` must be `True`) :param html_escape: If html tags in strings should be escaped. Default: True :return: the (formatted) name """ try: user = bot.get_chat_member(user_id, user_id).user except TgApiException: if id_fallback: name = "user#" + str(user_id) name = escape(name) if (name is not None and html_escape) else name if do_link: return '<a href="tg://user?id={id}">{name}</a>'.format( id=user_id, name=name) if id_tag: return "<{tag}>{name}</{tag}>".format(tag=id_tag, name=name) # end if # end if return None # end try return format_user(user, do_link=do_link, prefer_username=prefer_username, id_fallback=id_fallback, user_tag=user_tag, id_tag=id_tag, html_escape=html_escape)
def send(self, sender: PytgbotApiBot, receiver, reply_id)->PytgbotApiMessage: if self.receiver: receiver = self.receiver # end if if self.reply_id is not DEFAULT_MESSAGE_ID: reply_id = self.reply_id # end if try: return sender.forward_message( receiver, self.from_chat_id, self.msg_id, disable_notification=self.disable_notification ) except TgApiServerException as e: should_backoff(e) # checks if it should raise an DoRetryException raise # else it just raises as usual
def send(self, sender: PytgbotApiBot) -> PytgbotApiMessage: """ :param sender: The default value :param receiver: The default value :param reply_id: The default value :return: """ try: return sender.send_game( chat_id=self.receiver, game_short_name=self.game_short_name, disable_notification=self.disable_notification, reply_to_message_id=self.reply_id, reply_markup=self.reply_markup ) except TgApiServerException as e: raise # else it just raises as usual
def send(self, sender: PytgbotApiBot, receiver, reply_id)->PytgbotApiMessage: if self.receiver: receiver = self.receiver # end if if self.reply_id is not DEFAULT_MESSAGE_ID: reply_id = self.reply_id # end if self.prepare_file() try: return sender.send_document( receiver, self.file, caption=self.caption, reply_to_message_id=reply_id, reply_markup=self.reply_markup, disable_notification=self.disable_notification ) except TgApiServerException as e: should_backoff(e) # checks if it should raise an DoRetryException raise # else it just raises as usual
def send(self, sender: PytgbotApiBot, receiver, reply_id) -> PytgbotApiMessage: if self.receiver: receiver = self.receiver # end if if self.reply_id is not DEFAULT_MESSAGE_ID: reply_id = self.reply_id # end if try: return sender.forward_message( receiver, self.from_chat_id, self.msg_id, disable_notification=self.disable_notification) except TgApiServerException as e: should_backoff(e) # checks if it should raise an DoRetryException raise # else it just raises as usual
def send(self, sender: PytgbotApiBot) -> PytgbotApiMessage: try: result = sender.send_message( chat_id=self.receiver, text=self.text, parse_mode=self.parse_mode, disable_notification=self.disable_notification, reply_to_message_id=self.reply_id, reply_markup=self.reply_markup, disable_web_page_preview=self.disable_web_page_preview, allow_sending_without_reply=True, ) except TgApiServerException as e: raise # else it just raises as usual # end try # if result and self._next_msg: # # pass current message as reply id. # self._next_msg.reply_id == result # # end if return result
def retrieve_and_is_member(bot: Bot, chat_id: int, user_id: int) -> bool: """ Checks if a user is still in a chat/group. If lookup fails, `False` is assumed. :param bot: The bot to execute the get_chat_member request with. :param chat_id: id of the chat the user should be in :param user_id: id of the user :return: """ try: chat_member = bot.get_chat_member(chat_id, user_id) logger.debug(f'get_chat_member result: {chat_member}') return is_member(chat_member) except TgApiException: logger.exception( 'calling get_chat_member failed. Defaulting to \'no admin\'.') return False
def send(self, sender: PytgbotApiBot, receiver, reply_id) -> PytgbotApiMessage: if self.receiver: receiver = self.receiver # end if if self.reply_id is not DEFAULT_MESSAGE_ID: reply_id = self.reply_id # end if self.prepare_file() try: return sender.send_document( receiver, self.file, caption=self.caption, reply_to_message_id=reply_id, reply_markup=self.reply_markup, disable_notification=self.disable_notification) except TgApiServerException as e: should_backoff(e) # checks if it should raise an DoRetryException raise # else it just raises as usual
def init_bot(self): """ Creates the bot, and retrieves information about the bot itself (username, user_id) from telegram. :return: """ if not self.bot: # so you can manually set it before calling `init_app(...)`, # e.g. a mocking bot class for unit tests self.bot = Bot(self.__api_key, return_python_objects=self._return_python_objects) elif self.bot.return_python_objects != self._return_python_objects: # we don't have the same setting as the given one raise ValueError("The already set bot has return_python_objects {given}, but we have {our}".format( given=self.bot.return_python_objects, our=self._return_python_objects )) # end def myself = self.bot.get_me() if self.bot.return_python_objects: self._user_id = myself.id self._username = myself.username else: self._user_id = myself["result"]["id"] self._username = myself["result"]["username"]
def send(self, sender: PytgbotApiBot, receiver, reply_id)->PytgbotApiMessage: if self.receiver: receiver = self.receiver # end if if self.reply_id is not DEFAULT_MESSAGE_ID: reply_id = self.reply_id # end if try: result = sender.send_message( receiver, self.text, parse_mode=self.parse_mode, disable_notification=self.disable_notification, reply_to_message_id=reply_id, reply_markup=self.reply_markup, disable_web_page_preview=self.disable_web_page_preview ) except TgApiServerException as e: should_backoff(e) # checks if it should raise an DoRetryException raise # else it just raises as usual # end try # if result and self._next_msg: # # pass current message as reply id. # self._next_msg.reply_id == result # # end if return result
def retrieve_and_is_admin(bot: Bot, chat_id: int, user_id: int, right: Union[None, str] = None) -> bool: """ Checks if a user is admin (or even creator) of a chat/group. Optionally a specific right can be requested to exist. If lookup fails, `False` is assumed. :param bot: The bot to execute the get_chat_member request with. :param chat_id: :param user_id: :param right: Right to require, e.g. `"can_promote_members"`. :return: """ try: chat_member = bot.get_chat_member(chat_id, user_id) logger.debug(f'get_chat_member result: {chat_member}') return is_admin(chat_member, right=right) except TgApiException: logger.exception( 'calling get_chat_member failed. Defaulting to \'no admin\'.') return False
def send(self, sender: PytgbotApiBot, receiver, reply_id) -> bool: if self.receiver: receiver = self.receiver # end if return sender.send_chat_action(receiver, self.status)
def actual_sending(self, sender: PytgbotApiBot, receiver, reply_id): return sender.send_sticker( receiver, self.file_id, reply_to_message_id=reply_id, reply_markup=self.reply_markup, disable_notification=self.disable_notification )
# -*- coding: utf-8 -*- __author__ = 'luckydonald' import logging logger = logging.getLogger(__name__) from pytgbot import Bot, InputFile from somewhere import API_KEY # so I don't upload them to github :D # Just remove the line, and add API_KEY="..." and TEST_CHAT = 12345 # get you bot instance. bot = Bot(API_KEY) my_info=bot.get_me() print("Information about myself: {info}".format(info=my_info)) last_update_id = 0 while True: # loop forever. for update in bot.get_updates(limit=100, offset=last_update_id+1)["result"]: last_update_id = update["update_id"] print(update) if "message" in update and "text" in update["message"]: # we have a text message. if "chat" in update["message"]: # is a group chat sender = update["message"]["chat"]["id"] else: # user chat sender = update["message"]["from"]["id"] #end if
# -*- coding: utf-8 -*- __author__ = 'luckydonald' import logging logger = logging.getLogger(__name__) from pytgbot import Bot from examples.somewhere import API_KEY # so I don't upload them to github :D # Just remove the line, and add API_KEY="..." and TEST_CHAT = 12345 # get you bot instance. bot = Bot(API_KEY) my_info=bot.get_me() print("Information about myself: {info}".format(info=my_info)) last_update_id = 0 while True: # loop forever. for update in bot.get_updates(limit=100, offset=last_update_id+1)["result"]: last_update_id = update["update_id"] print(update) if "message" in update and "text" in update["message"]: # we have a text message. if update["message"]["text"] == "/ping": if "chat" in update["message"]: # is a group chat bot.send_msg(update["message"]["chat"]["id"], "group pong!") else: # user chat bot.send_msg(update["message"]["from"]["id"], "user pong!")
# -*- coding: utf-8 -*- __author__ = 'luckydonald' import logging logger = logging.getLogger(__name__) logging.basicConfig(level=logging.DEBUG) from pytgbot import Bot from somewhere import API_KEY # just set the key manually. # API_KEY = "1231412:adLsIfTsTsLfEfPdIdPwdwIoSaBgEuSzTwszPdOaNdYs" bot = Bot(API_KEY) last_update_id = -1 while True: # loop forever. for update in bot.get_updates(limit=100, offset=last_update_id+1)["result"]: last_update_id = update["update_id"] if not "message" in update: continue message = update["message"] logger.info("got message: {msg}".format(msg=message)) if not "text" in message: continue logger.info("got message: {msg}".format(msg=message.text.encode("utf-8"))) if "reply_to_message" in message and message.text == "/pics": name = message.reply_to_message["from"].first_name peer = message.reply_to_message["from"].id origin = message.chat.id if "chat" in message else message["from"].id for x in reversed(bot.get_user_profile_photos(peer)["result"]["photos"]):
from pytgbot.api_types.receivable.inline import InlineQuery from pytgbot.api_types.receivable.updates import Update from pytgbot.exceptions import TgApiException __author__ = 'luckydonald' import logging logger = logging.getLogger(__name__) from somewhere import API_KEY # so I don't upload them to github :D # Just remove the line, and add API_KEY="..." from pytgbot import Bot from luckydonaldUtils.encoding import to_native as n # get you bot instance. bot = Bot(API_KEY) my_info=bot.get_me() print("Information about myself: {info}".format(info=my_info)) last_update_id = 0 while True: # loop forever. for update in bot.get_updates(limit=100, offset=last_update_id+1, error_as_empty=True): assert isinstance(update, Update) last_update_id = update.update_id print(update) if not update.inline_query: continue query_obj = update.inline_query assert isinstance(query_obj, InlineQuery)
# -*- coding: utf-8 -*- __author__ = 'luckydonald' import logging logger = logging.getLogger(__name__) from pytgbot import Bot from somewhere import API_KEY # so I don't upload them to github :D # Just remove the line, and add API_KEY="..." and TEST_CHAT = 12345 # get you bot instance. bot = Bot(API_KEY) my_info=bot.get_me() print("Information about myself: {info}".format(info=my_info)) last_update_id = 0 while True: # loop forever. for update in bot.get_updates(limit=100, offset=last_update_id+1): last_update_id = update.update_id print(update) if update.message and update.message.text: # we have a text message. if update.message.chat: # is a group chat sender = update.message.chat.id else: # user chat sender = update.message.from_peer.id # end if
class TeleflaskBase(TeleflaskMixinBase): VERSION = VERSION __version__ = VERSION def __init__(self, api_key, app=None, blueprint=None, # FlaskTgBot kwargs: hostname=None, hostpath=None, hookpath="/income/{API_KEY}", debug_routes=False, disable_setting_webhook=False, # pytgbot kwargs: return_python_objects=True): """ A new Teleflask(Base) object. :param api_key: The key for the telegram bot api. :type api_key: str :param app: The flask app if you don't like to call :meth:`init_app` yourself. :type app: flask.Flask | None :param blueprint: A blueprint, where the telegram webhook (and the debug endpoints, see `debug_routes`) will be registered in. Use if you don't like to call :meth:`init_app` yourself. If not set, but `app` is, it will register any routes to the `app` itself. :type blueprint: flask.Blueprint | None :param hostname: The hostname or IP (and maybe a port) where this server is reachable in the internet. Specify the path with :param hostpath: Used to calculate the webhook url. Also configurable via environment variables. See calculate_webhook_url() :param hostpath: The host url the base of where this bot is reachable. Examples: None (for root of server) or "/bot2" Note: The webhook will only be set on initialisation. Also configurable via environment variables. See calculate_webhook_url() :param hookpath: The endpoint of the telegram webhook. Defaults to "/income/<API_KEY>" Note: The webhook will only be set on initialisation. Also configurable via environment variables. See calculate_webhook_url() :param debug_routes: Add extra url endpoints usefull for debugging. See setup_routes(...) :param disable_setting_webhook: Disable updating the webhook when starting. Useful for unit tests. :param return_python_objects: Enable return_python_objects in pytgbot. See pytgbot.bot.Bot """ self.__api_key = api_key self.bot = None # will be set in self.init_bot() self.update_listener = list() self.commands = dict() self._return_python_objects = return_python_objects self._webhook_url = None # will be filled out by self.calculate_webhook_url() in self.init_app(...) self.hostname = hostname # e.g. "example.com:443" self.hostpath = hostpath self.hookpath = hookpath self.disable_setting_webhook=disable_setting_webhook if app or blueprint: self.init_app(app, blueprint=blueprint, debug_routes=debug_routes) # end if # end def def init_bot(self): """ Creates the bot, and retrieves information about the bot itself (username, user_id) from telegram. :return: """ if not self.bot: # so you can manually set it before calling `init_app(...)`, # e.g. a mocking bot class for unit tests self.bot = Bot(self.__api_key, return_python_objects=self._return_python_objects) elif self.bot.return_python_objects != self._return_python_objects: # we don't have the same setting as the given one raise ValueError("The already set bot has return_python_objects {given}, but we have {our}".format( given=self.bot.return_python_objects, our=self._return_python_objects )) # end def myself = self.bot.get_me() if self.bot.return_python_objects: self._user_id = myself.id self._username = myself.username else: self._user_id = myself["result"]["id"] self._username = myself["result"]["username"] # end if # end def def init_app(self, app, blueprint=None, debug_routes=False): """ Gives us access to the flask app (and optionally provide a Blueprint), where we will add a routing endpoint for the telegram webhook. Calls `self.init_bot()`. :param app: the :class:`flask.Flask` app :type app: flask.Flask :param blueprint: A blueprint, where the telegram webhook (and the debug endpoints, see `debug_routes`) will be registered in. If `None` was provided, it will register any routes to the `app` itself. :type blueprint: flask.Blueprint | None :param debug_routes: Add extra url endpoints, useful for debugging. See setup_routes(...) :type debug_routes: bool :return: None :rtype: None """ self.app = app self.blueprint = blueprint self.init_bot() hookpath, self._webhook_url = self.calculate_webhook_url(hostname=self.hostname, hostpath=self.hostpath, hookpath=self.hookpath) self.setup_routes(hookpath=hookpath, debug_routes=debug_routes) self.set_webhook() # this will set the webhook in the bot api. def calculate_webhook_url(self, hostname=None, hostpath=None, hookpath="/income/{API_KEY}"): """ Calculates the webhook url. Please note, this doesn't change any registered view function! Returns a tuple of the hook path (the url endpoint for your flask app) and the full webhook url (for telegram) Note: Both can include the full API key, as replacement for ``{API_KEY}`` in the hookpath. :Example: Your bot is at ``https://example.com:443/bot2/``, you want your flask to get the updates at ``/tg-webhook/{API_KEY}``. This means Telegram will have to send the updates to ``https://example.com:443/bot2/tg-webhook/{API_KEY}``. You now would set hostname = "example.com:443", hostpath = "/bot2", hookpath = "/tg-webhook/{API_KEY}" Note: Set ``hostpath`` if you are behind a reverse proxy, and/or your flask app root is *not* at the web server root. :param hostname: A hostname. Without the protocol. Examples: "localhost", "example.com", "example.com:443" If None (default), the hostname comes from the URL_HOSTNAME environment variable, or from http://ipinfo.io if that fails. :param hostpath: The path after the hostname. It must start with a slash. Use this if you aren't at the root at the server, i.e. use url_rewrite. Example: "/bot2" If None (default), the path will be read from the URL_PATH environment variable, or "" if that fails. :param hookpath: Template for the route of incoming telegram webhook events. Must start with a slash. The placeholder {API_KEY} will replaced with the telegram api key. Note: This doesn't change any routing. You need to update any registered @app.route manually! :return: the tuple of calculated (hookpath, webhook_url). :rtype: tuple """ import os, requests # # # # try to fill out empty arguments # # if not hostname: hostname = os.getenv('URL_HOSTNAME', None) # end if if hostpath is None: hostpath = os.getenv('URL_PATH', "") # end if if not hookpath: hookpath = "/income/{API_KEY}" # end if # # # # check if the path looks at least a bit valid # # logger.debug("hostname={hostn!r}, hostpath={hostp!r}, hookpath={hookp!r}".format( hostn=hostname, hostp=hostpath, hookp=hookpath )) if hostname: if hostname.endswith("/"): raise ValueError("hostname can't end with a slash: {value}".format(value=hostname)) # end if if hostname.startswith("https://"): hostname = hostname[len("https://"):] logger.warning("Automatically removed \"https://\" from hostname. Don't include it.") # end if if hostname.startswith("http://"): raise ValueError("Don't include the protocol ('http://') in the hostname. " "Also telegram doesn't support http, only https.") # end if else: # no hostname info = requests.get('http://ipinfo.io').json() hostname = str(info["ip"]) logger.warning("URL_HOSTNAME env not set, falling back to ip address: {ip!r}".format(ip=hostname)) # end if if not hostpath == "" and not hostpath.startswith("/"): logger.info("hostpath didn't start with a slash: {value!r} Will be added automatically".format(value=hostpath)) hostpath = "/" + hostpath # end def if not hookpath.startswith("/"): raise ValueError("hookpath must start with a slash: {value!r}".format(value=hostpath)) # end def hookpath = hookpath.format(API_KEY=self.__api_key) if not hostpath: logger.info("URL_PATH is not set.") webhook_url = "https://{hostname}{hostpath}{hookpath}".format(hostname=hostname, hostpath=hostpath, hookpath=hookpath) logger.debug("Tele={hostn!r}, hostpath={hostp!r}, hookpath={hookp!r}".format( hostn=hostname, hostp=hostpath, hookp=hookpath )) return hookpath, webhook_url # end def @property def username(self): return self._username # end def @property def user_id(self): return self._user_id # end def @property def webhook_url(self): return self._webhook_url # end def def set_webhook(self): """ Sets the telegram webhook. Checks Telegram if there is a webhook set, and if it needs to be changed. :return: """ assert isinstance(self.bot, Bot) existing_webhook = self.bot.get_webhook_info() if self._return_python_objects: from pytgbot.api_types.receivable import WebhookInfo assert isinstance(existing_webhook, WebhookInfo) webhook_url = existing_webhook.url webhook_meta = existing_webhook.to_array() else: webhook_url = existing_webhook["result"]["url"] webhook_meta = existing_webhook["result"] # end def del existing_webhook logger.info("Last webhook pointed to {url!r}.\nMetadata: {hook}".format( url=self.hide_api_key(webhook_url), hook=self.hide_api_key("{!r}".format(webhook_meta)) )) if webhook_url == self.webhook_url: logger.info("Webhook set correctly. No need to change.") else: if not self.app.config.get("DISABLE_SETTING_TELEGRAM_WEBHOOK", False): logger.info("Setting webhook to {url}".format(url=self.hide_api_key(self.webhook_url))) logger.debug(self.bot.set_webhook(url=self.webhook_url)) else: logger.info("Would set webhook to {url!r}, but is disabled by DISABLE_SETTING_TELEGRAM_WEBHOOK config.".format(url=self.hide_api_key(self.webhook_url))) # end if # end if # end def def do_startup(self): """ This code is executed after server boot. Sets the telegram webhook (see :meth:`set_webhook(self)`) and calls `super().do_setup()` for the superclass (e.g. other mixins) :return: """ self.init_bot() # retrieve username, user_id from telegram self.set_webhook() # register telegram webhook super().do_startup() # do more registered startup actions. # end def def hide_api_key(self, string): """ Replaces the api key with "<API_KEY>" in a given string. Note: if the given object is no string, :meth:`str(object)` is called first. :param string: The str which can contain the api key. :return: string with the key replaced """ if not isinstance(string, str): string = str(string) # end if return string.replace(self.__api_key, "<API_KEY>") # end def def jsonify(self, func): """ Decorator. Converts the returned value of the function to json, and sets mimetype to "text/json". It will also automatically replace the api key where found in the output with "<API_KEY>". Usage: @app.route("/foobar") @app.jsonify def foobar(): return {"foo": "bar"} # end def # app is a instance of this class There are some special cases to note: - :class:`tuple` is interpreted as (data, status). E.g. return {"error": "not found"}, 404 would result in a 404 page, with json content {"error": "not found"} - :class:`flask.Response` will be returned directly, except it is in a :class:`tuple` In that case the status code of the returned response will be overwritten by the second tuple element. - :class:`TgBotApiObject` will be converted to json too. Status code 200. - An exception will be returned as `{"error": "exception raised"}` with status code 503. :param func: the function to wrap :return: the wrapped function returning json responses. """ from functools import wraps from flask import Response import json logger.debug("func: {}".format(func)) @wraps(func) def jsonify_inner(*args, **kwargs): try: result = func(*args, **kwargs) except: logger.exception("failed executing {name}.".format(name=func.__name__), exc_info=True) result = {"error": "exception raised"}, 503 # end def status = None # will be 200 if not otherwise changed if isinstance(result, tuple): response, status = result else: response = result # end if if isinstance(response, Response): if status: response.status_code = status # end if return response # end if if isinstance(response, TgBotApiObject): response = response.to_array() # end if response = json.dumps(response) # end if assert isinstance(response, str) response_kwargs = {} response_kwargs.setdefault("mimetype", "text/json") if status: response_kwargs["status"] = status # end if res = Response(self.hide_api_key(response), **response_kwargs) logger.debug("returning: {}".format(res)) return res # end def inner return jsonify_inner # end def @_self_jsonify def view_exec(self, api_key, command): """ Issue commands. E.g. /exec/TELEGRAM_API_KEY/getMe :param api_key: gets checked, so you can't just execute commands. :param command: the actual command :return: """ if api_key != self.__api_key: error_msg = "Wrong API key: {wrong_key!r}".format(wrong_key=api_key) logger.warning(error_msg) return {"status": "error", "message": error_msg, "error_code": 403}, 403 # end if from flask import request from pytgbot.exceptions import TgApiServerException logger.debug("COMMAND: {cmd}, ARGS: {args}".format(cmd=command, args=request.args)) try: res = self.bot.do(command, **request.args) if self._return_python_objects: return res.to_array() else: return res # end if except TgApiServerException as e: return {"status": "error", "message": e.description, "error_code": e.error_code}, e.error_code # end try # end def @_self_jsonify def view_status(self): """ Returns the status about the bot's webhook. :return: webhook info """ try: res = self.bot.get_webhook_info() # TODO: fix to work with return_python_objects==False return res.to_array() except TgApiServerException as e: return {"status": "error", "message": e.description, "error_code": e.error_code}, e.error_code # end try @_self_jsonify def view_updates(self): """ This processes incoming telegram updates. :return: """ from pprint import pformat from flask import request from pytgbot.api_types.receivable.updates import Update logger.debug("INCOME:\n{}\n\nHEADER:\n{}".format( pformat(request.get_json()), request.headers if hasattr(request, "headers") else None )) update = Update.from_array(request.get_json()) try: result = self.process_update(update) except Exception as e: logger.exception("process_update()") result = {"status": "error", "message": str(e)} result = result if result else {"status": "probably ok"} logger.info("returning result: {}".format(result)) return result # end def @_self_jsonify def view_hostinfo(self): """ Get infos about your host, like IP etc. :return: """ import socket import requests info = requests.get('http://ipinfo.io').json() info["host"] = socket.gethostname() info["version"] = self.VERSION return info # end def def get_router(self): """ Where to call `add_url_rule` (aka. `@route`) on. Returns either the blueprint if there is any, or the app. :raises ValueError: if neither blueprint nor app is set. :returns: either the blueprint if it is set, or the app. :rtype: flask.Blueprint | flask.Flask """ if self.blueprint: return self.blueprint # end if if not self.app: raise ValueError("The app (self.app) is not set.") # end if return self.app def setup_routes(self, hookpath, debug_routes=False): """ Sets the pathes to the registered blueprint/app: - "webhook" (self.view_updates) at hookpath Also, if `debug_routes` is `True`: - "exec" (self.view_exec) at "/exec/API_KEY/<command>" (`API_KEY` is filled in, `<command>` is any Telegram API command.) - "status" (self.view_status) at "/status" - "hostinfo" (self.view_hostinfo) at "/hostinfo" :param hookpath: The path where it expects telegram updates to hit the flask app/blueprint. :type hookpath: str :param debug_paths: Add several debug pathes. :type debug_routes: bool """ # Todo: Find out how to handle blueprints if not self.app and not self.blueprint: raise ValueError("No app (self.app) or Blueprint (self.blueprint) was set.") # end if router = self.get_router() logger.info("Adding webhook route: {url!r}".format(url=hookpath)) router.add_url_rule(hookpath, endpoint="webhook", view_func=self.view_updates, methods=['POST']) if debug_routes: router.add_url_rule("/exec/{api_key}/<command>".format(api_key=self.__api_key), endpoint="exec" , view_func=self.view_exec) router.add_url_rule("/status", endpoint="status", view_func=self.view_status) router.add_url_rule("/hostinfo", endpoint="hostinfo", view_func=self.view_hostinfo) # end if # end def @abc.abstractmethod def process_update(self, update): return # end def def process_result(self, update, result): """ Send the result. It may be a :class:`Message` or a list of :class:`Message`s Strings will be send as :class:`TextMessage`, encoded as raw text. :param manager: :param result: :return: """ from ..messages import Message reply_to, reply_id = None, None if update.message and update.message.chat.id and update.message.message_id: reply_to, reply_id = update.message.chat.id, update.message.message_id # end if if update.channel_post and update.channel_post.chat.id and update.channel_post.message_id: reply_to, reply_id = update.channel_post.chat.id, update.channel_post.message_id # end if if update.edited_message and update.edited_message.chat.id and update.edited_message.message_id: reply_to, reply_id = update.edited_message.chat.id, update.edited_message.message_id # end if if update.edited_channel_post and update.edited_channel_post.chat.id and update.edited_channel_post.message_id: reply_to, reply_id = update.edited_channel_post.chat.id, update.edited_channel_post.message_id # end if if isinstance(result, (Message, str, list, tuple)): self.send_message(result, reply_to, reply_id) elif result is False or result is None: logger.debug("Ignored result {res!r}".format(res=result)) # ignore it else: logger.warn("Unexpected plugin result: {type}".format(type=type(result))) # end if # end def def send_message(self, message, reply_to, reply_id): """ Sends a Message. Plain strings will become an unformatted TextMessage. Supports to mass send lists, tuples, Iterable. :param message: A Message object. :type message: Message | str | list | tuple | :param instant: Send without waiting for the plugin's function to be done. True to send as soon as possible. False or None to wait until the plugin's function is done and has returned, messages the answers in a bulk. :type instant: bool or None """ from pytgbot.exceptions import TgApiException from ..messages import Message, TextMessage logger.debug("Got {}".format(message)) if not isinstance(message, (Message, str, list, tuple)): raise TypeError("Is not a Message type (or str or tuple/list).") # end if if isinstance(message, tuple): message = [x for x in message] # end if if not isinstance(message, list): message = [message] # end if assert isinstance(message, list) for msg in message: if isinstance(msg, str): assert not isinstance(message, str) # because we would split a string to pieces. msg = TextMessage(msg, parse_mode="text") # end if if not isinstance(msg, Message): raise TypeError("Is not a Message type.") # end if # if msg._next_msg: # TODO: Reply message? # message.insert(message.index(msg) + 1, msg._next_msg) # msg._next_msg = None from requests.exceptions import RequestException try: msg.send(self.bot, reply_to, reply_id) except (TgApiException, RequestException): logger.exception("Manager failed messages. Message was {msg!s}".format(msg=msg))
def send(self, sender: PytgbotApiBot, receiver, reply_id)->bool: if self.receiver: receiver = self.receiver # end if return sender.send_chat_action(receiver, self.status)
import time import types config = configparser.ConfigParser() scriptdir= os.path.dirname(sys.argv[0]) if scriptdir == "": scriptdir = os.getcwd() conffile = scriptdir + '/socialstats.config' config.read(conffile) BOT_TOKEN = config.get('telegram','BOT_TOKEN') CHAT_ID = config.get('telegram','CHAT_ID') igusers = config.items('instagram') dbFilePath = scriptdir + "/" + config.get('storage','igdbFilePath') bot = Bot(BOT_TOKEN) try: with open(dbFilePath,'r') as dbFile: fotos = json.load(dbFile) except FileNotFoundError: print("Db file not found, we may be running for the first time. We'll create a new dbfile") fotos = {} now = datetime.date.today().strftime('%Y%m%d') limit=1 for iguser, telegram_id in igusers: cursor=0 max_id=""
# -*- coding: utf-8 -*- __author__ = 'luckydonald' import logging logger = logging.getLogger(__name__) from pytgbot import Bot from somewhere import API_KEY # just set the key manually. # API_KEY = "1231412:adLsIfTsTsLfEfPdIdPwdwIoSaBgEuSzTwszPdOaNdYs" # get a bot instance bot = Bot(API_KEY) last_update_id = -1 while True: # loop forever. for update in bot.get_updates(limit=100, offset=last_update_id+1, poll_timeout=30): last_update_id = update.update_id print("got message: {msg}".format(msg=update)) if not "message" in update: continue message = update.message if not "text" in message: continue logger.debug("got text: {msg}".format(msg=message.text.encode("utf-8"))) if not message.text == "/pics": continue origin = message.chat.id if "chat" in message else message.from_peer.id if "reply_to_message" in message:
import sys reload(sys) sys.setdefaultencoding('utf-8') import logging logger = logging.getLogger(__name__) from pytgbot import Bot, InputFile import facebook API_KEY="..." graph = facebook.GraphAPI(access_token='...') TEST_CHAT = 12345 bot = Bot(API_KEY) my_info=bot.get_me() print("Informacion del Bot: {info}".format(info=my_info)) last_update_id = 0 while True: # loop forever. for update in bot.get_updates(limit=100, offset=last_update_id+1)["result"]: last_update_id = update["update_id"] print(update) if "message" in update and "text" in update["message"]: if "chat" in update["message"]: sender = update["message"]["chat"]["id"] else:
def setUp(self) -> None: self.api_bot = Bot(TEST_BOT_AUTH) self.user_bot = Bot(TEST_USER_AUTH)
import configparser import os import sys from operator import itemgetter config = configparser.ConfigParser() scriptdir= os.path.dirname(sys.argv[0]) if scriptdir == "": scriptdir = os.getcwd() conffile = scriptdir + '/socialstats.config' config.read(conffile) BOT_TOKEN = config.get('telegram','BOT_TOKEN') CHAT_ID = config.get('telegram','CHAT_ID') bot = Bot(BOT_TOKEN) googleapikey = config.get ('youtube','googleapikey') channelid = config.get ('youtube','channelid') truncatelimit = 25 dbFilePath = scriptdir + "/" + config.get('storage','ytdbFilePath') url="https://www.googleapis.com/youtube/v3/search?key="+ googleapikey + "&channelId="+channelid+"&type=video&part=snippet&order=viewCount&maxResults=50" r = requests.get(url) data = json.loads(r.text) videolist="" for video in data['items']: videolist = videolist + video['id']['videoId'] + ","