nmlorg / metabot

Modularized, multi-account bot.
https://metabot.readthedocs.io/
6 stars 0 forks source link

Make Bot instances available cheaply outside of update callbacks #26

Open nmlorg opened 5 years ago

nmlorg commented 5 years ago

The next step for #25 requires accessing an ntelebot.bot.Bot instance from outside of a callback handling an update received by polling it, roughly:

     def _hourly():
         multibot.multical.poll()
+        for botconf in multibot.bots.values():
+            for groupid, groupconf in botconf['moderator'].items():
+                if groupconf.get('daily') and groupconf.get('timezone'):
+                    now = datetime.datetime.now(pytz.timezone(groupconf['timezone']))
+                    if now.hour == 8:
+                        bot = ...  # <---
+                        events = _get_group_events(bot, groupconf)
+                        if events:
+                            bot.send_message(groupid, 'Upcoming events:\n' + '\n'.join(events))
         _queue()
  1. This could literally be bot = ntelebot.bot.Bot(botconf['telegram']['token']), but that would result in an extra call to getMe every day for every so-configured group. a. This could be alleviated by storing pre-built Bot instances in a dict, so it would just be an extra call to getMe every day for every bot (rather than for every group). b. This could be further reduced by using a global dict, so it would just be an extra call to getMe every restart (rather than every day) for every bot.
  2. We could change ntelebot.loop.Loop.active to be a dict of token -> Bot instance, rather than just a set of bot tokens, so we'd iterate over something like:
             multibot.multical.poll()
    +        for bot in multibot.loop.bots.values():
    +            botconf = multibot.bots[bot.username]
    +            for groupid, groupconf in botconf['moderator'].items():
  3. We could store Bot instances in multibot.bots, which might be as simple as literally setting multibot.bots[bot.username].bot = bot, so in the original version we could just use bot = botconf.bot. a. Alternatively, create something new like multibot.botinstances (or even go ahead with #18, moving metabot.bots to metabot.config['bots'] and then reusing metabot.bots for this).
  4. A version of (1) that wouldn't require any additional getMe calls might be to add a memoized factory in front of Bot construction, so rather than ever doing bot = ntelebot.bot.Bot(token) we always use bot = ntelebot.get_bot(token) or bot = ntelebot.bot.Bot.get(token).
nmlorg commented 2 months ago
2024-06-30 09:40:04,692 INFO reminders.py:41] Running periodic.
2024-06-30 09:40:24,852 ERROR loop.py:72] Ignoring uncaught error while dispatching:
Traceback (most recent call last):
  File "lib/python3.10/site-packages/urllib3/connectionpool.py", line 449, in _make_request
    six.raise_from(e, None)
  File "<string>", line 3, in raise_from
  File "lib/python3.10/site-packages/urllib3/connectionpool.py", line 444, in _make_request
    httplib_response = conn.getresponse()
  File "/usr/lib/python3.10/http/client.py", line 1375, in getresponse
    response.begin()
  File "/usr/lib/python3.10/http/client.py", line 318, in begin
    version, status, reason = self._read_status()
  File "/usr/lib/python3.10/http/client.py", line 279, in _read_status
    line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
  File "/usr/lib/python3.10/socket.py", line 705, in readinto
    return self._sock.recv_into(b)
  File "/usr/lib/python3.10/ssl.py", line 1303, in recv_into
    return self.read(nbytes, buffer)
  File "/usr/lib/python3.10/ssl.py", line 1159, in read
    return self._sslobj.read(len, buffer)
TimeoutError: The read operation timed out

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "lib/python3.10/site-packages/requests/adapters.py", line 489, in send
    resp = conn.urlopen(
  File "lib/python3.10/site-packages/urllib3/connectionpool.py", line 787, in urlopen
    retries = retries.increment(
  File "lib/python3.10/site-packages/urllib3/util/retry.py", line 550, in increment
    raise six.reraise(type(error), error, _stacktrace)
  File "lib/python3.10/site-packages/urllib3/packages/six.py", line 770, in reraise
    raise value
  File "lib/python3.10/site-packages/urllib3/connectionpool.py", line 703, in urlopen
    httplib_response = self._make_request(
  File "lib/python3.10/site-packages/urllib3/connectionpool.py", line 451, in _make_request
    self._raise_timeout(err=e, url=url, timeout_value=read_timeout)
  File "lib/python3.10/site-packages/urllib3/connectionpool.py", line 340, in _raise_timeout
    raise ReadTimeoutError(
urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='api.telegram.org', port=443): Read timed out. (read timeout=12)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "lib/python3.10/site-packages/ntelebot/bot.py", line 58, in __call__
    data = requests.post(self.url, timeout=self.timeout, **_prepare(params)).json()
  File "lib/python3.10/site-packages/requests/api.py", line 115, in post
    return request("post", url, data=data, json=json, **kwargs)
  File "lib/python3.10/site-packages/requests/api.py", line 59, in request
    return session.request(method=method, url=url, **kwargs)
  File "lib/python3.10/site-packages/requests/sessions.py", line 587, in request
    resp = self.send(prep, **send_kwargs)
  File "lib/python3.10/site-packages/requests/sessions.py", line 701, in send
    r = adapter.send(request, **kwargs)
  File "lib/python3.10/site-packages/requests/adapters.py", line 578, in send
    raise ReadTimeout(e, request=request)
requests.exceptions.ReadTimeout: HTTPSConnectionPool(host='api.telegram.org', port=443): Read timed out. (read timeout=12)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "lib/python3.10/site-packages/ntelebot/loop.py", line 70, in run
    callback()
  File "lib/python3.10/site-packages/metabot/modules/reminders.py", line 44, in periodic
    _daily_messages(multibot, records)
  File "lib/python3.10/site-packages/metabot/modules/reminders.py", line 91, in _daily_messages
    [eventutil.format_event(bot, event, tzinfo, full=False) for event in events])
  File "lib/python3.10/site-packages/metabot/modules/reminders.py", line 91, in <listcomp>
    [eventutil.format_event(bot, event, tzinfo, full=False) for event in events])
  File "lib/python3.10/site-packages/metabot/util/eventutil.py", line 67, in format_event
    bot.encode_url('/events %s %s' %
  File "lib/python3.10/site-packages/ntelebot/bot.py", line 47, in encode_url
    return ntelebot.deeplink.encode_url(self.username, command)
  File "lib/python3.10/site-packages/ntelebot/bot.py", line 36, in username
    self._username = self.get_me()['username']
  File "lib/python3.10/site-packages/ntelebot/bot.py", line 60, in __call__
    raise ntelebot.errors.Timeout(exc)
ntelebot.errors.Timeout: HTTPSConnectionPool(host='api.telegram.org', port=443): Read timed out. (read timeout=12)