def group(ctx, msg): """Handle /events in a group chat.""" group_id = '%s' % ctx.chat['id'] groupconf = ctx.bot.config['issue37']['moderator'][group_id] calcodes, tzinfo, count, days, unused_hour, unused_dow = eventutil.get_group_conf(groupconf) if not calcodes or not tzinfo: missing = [] if not calcodes: missing.append('choose one or more calendars') if not tzinfo: missing.append('set the time zone') return msg.add( "I'm not configured for this group! Ask a bot admin to go into the <b>moderator</b> " 'module settings, group <b>%s</b>, and %s.', group_id, humanize.list(missing)) events, unused_alerts = eventutil.get_group_events(ctx.bot, calcodes, tzinfo, count, days) if not events: msg.add('No events in the next %s days!', days) else: url = eventutil.get_image(events[0], ctx.bot.config) if url: msg.add('photo:' + url) msg.add('\n'.join( eventutil.format_event(ctx.bot, event, tzinfo, full=False) for event in events))
def process_event(ctx, msg, query): # pylint: disable=too-many-branches,too-many-locals """Process queries of the form "When is the next XXX?".""" if ctx.chat['type'] == 'private': conf = ctx.bot.config['issue37']['events']['users']['%s' % ctx.user['id']] else: conf = ctx.bot.config['issue37']['moderator']['%s' % ctx.chat['id']] calcodes = conf.get('calendars', '').split() timezone = pytz.timezone(conf.get('timezone', 'UTC')) did_first = did_cal = did_summary = did_venue = False summaries = set() venues = set() local_id = None while not did_first or not did_cal or not did_summary or not did_venue: _, event, nextev = ctx.bot.multibot.multical.get_event(local_id) if not event: break summary = event['summary'].lower() location = event['location'].lower() if query['event'] in summary and (query.get('location') or '') in location: venue = location.split(',', 1)[0] reasons = set() calcode = event['local_id'].split(':', 1)[0] if calcode not in calcodes: if not did_first: did_first = True reasons.add(query['event'] + ' I know about') elif not did_first: did_first = did_cal = True reasons.add(query['event']) elif not did_cal: did_cal = True reasons.add('%s on the %s calendar' % (query['event'], ctx.bot.multibot.calendars[calcode]['name'])) else: if not did_summary and summary not in summaries: did_summary = True reasons.add(event['summary']) if not did_venue and venue not in venues: did_venue = True reasons.add('%s at %s' % (query['event'], event['location'].split(',', 1)[0])) if reasons: msg.add('<i>The next %s is:</i>', ' / '.join(sorted(reasons))) msg.add(eventutil.format_event(ctx.bot, event, timezone, full=False)) summaries.add(summary) venues.add(venue) if not nextev: break local_id = nextev['local_id']
def private(ctx, msg, modconf): # pylint: disable=too-many-branches,too-many-locals """Handle /events in a private chat.""" eventid, timezone = ctx.split(2) if ':' in eventid and timezone: suffix = ' ' + timezone calcodes = eventid.split(':', 1)[0] else: suffix = '' user_id = '%s' % ctx.user['id'] userconf = modconf['users'][user_id] calcodes = userconf.get('calendars') timezone = userconf.get('timezone') if not calcodes or not timezone: missing = [] if not calcodes: missing.append('choose one or more calendars') if not timezone: missing.append('set your time zone') msg.add('Please %s!', humanize.list(missing)) return msg.button('Settings', '/events set') calendar_view = ctx.bot.multibot.multical.view(calcodes.split()) tzinfo = pytz.timezone(timezone) prevev, event, nextev = calendar_view.get_event(eventid) if not event: prevev, event, nextev = calendar_view.get_event() if not event: msg.add('No upcoming events!') else: msg.add(eventutil.format_event(ctx.bot, event, tzinfo, full=True)) if ctx.user['id'] in ctx.bot.config['issue37']['admin']['admins']: msg.button('Customize', '/events admin ' + event['local_id']) buttons = [None, ('Settings', '/events set'), None] if prevev: buttons[0] = ('Prev', '/events %s%s' % (prevev['local_id'], suffix)) if suffix: buttons[1] = ('My Events', '/events') elif event and event['local_id'] != calendar_view.current_local_id: buttons[1] = ('Current', '/events') if nextev: buttons[2] = ('Next', '/events %s%s' % (nextev['local_id'], suffix)) msg.buttons(buttons)
def _daily_messages(multibot, records): # pylint: disable=too-many-branches,too-many-locals,too-many-statements now = time.time() for botuser, botconf in multibot.conf['bots'].items(): # pylint: disable=too-many-nested-blocks for groupid, groupconf in botconf['issue37']['moderator'].items(): calcodes, tzinfo, count, days, hour, dow = eventutil.get_group_conf( groupconf) if not tzinfo or not isinstance(hour, int): continue # See https://github.com/nmlorg/metabot/issues/26. bot = ntelebot.bot.Bot(botconf['issue37']['telegram']['token']) bot.multibot = multibot form = lambda event: eventutil.format_event( bot, event, tzinfo, full=False) # pylint: disable=cell-var-from-loop title = lambda event: ' \u2022 ' + event['summary'] nowdt = datetime.datetime.fromtimestamp(now, tzinfo) key = (botuser, groupid) if nowdt.hour == hour and not dow & 1 << nowdt.weekday(): events, alerts = eventutil.get_group_events( bot, calcodes, tzinfo, count, days, now) _handle_alerts(bot, records, groupid, alerts) if events: preambles = groupconf['daily'].get('text', '').splitlines() preamble = (preambles and preambles[nowdt.toordinal() % len(preambles)] or '') text = _format_daily_message(preamble, list(map(form, events))) url = eventutil.get_image(events[0], botconf) message = None if url: try: message = bot.send_photo(chat_id=groupid, photo=url, caption=text, parse_mode='HTML', disable_notification=True) except ntelebot.errors.Error: logging.exception('Downgrading to plain text:') if not message: try: message = bot.send_message( chat_id=groupid, text=text, parse_mode='HTML', disable_web_page_preview=True, disable_notification=True) except ntelebot.errors.Error: logging.exception('While sending to %s:\n%s', groupid, text) if message: records[key] = (now, [event.copy() for event in events], message) elif key in records: lastnow, lastevents, lastmessage = records[key] lastmap = {event['local_id']: event for event in lastevents} events, alerts = eventutil.get_group_events( bot, calcodes, tzinfo, count, days, lastnow) _handle_alerts(bot, records, groupid, alerts) curmap = {event['local_id']: event for event in events} bothevents = events.copy() bothevents.extend(event for event in lastevents if event['local_id'] not in curmap) bothevents.sort(key=operator.itemgetter( 'start', 'end', 'summary', 'local_id')) edits = [] for event in bothevents: lastevent = lastmap.get(event['local_id']) if not lastevent: edits.append(title(event)) edits.append(' \u25e6 New event!') continue event = multibot.multical.get_event(event['local_id'])[1] if not event: edits.append(title(lastevent)) edits.append(' \u25e6 Removed.') continue pairs = ( (lastevent['summary'], event['summary']), (eventutil.humanize_range(lastevent['start'], lastevent['end'], tzinfo), eventutil.humanize_range(event['start'], event['end'], tzinfo)), (lastevent['location'], event['location']), (html.sanitize(lastevent['description'], strip=True), html.sanitize(event['description'], strip=True)), ) # yapf: disable pieces = [] for left, right in pairs: diff = _quick_diff(left, right) if diff: pieces.append(diff) if pieces: edits.append(title(event)) for left, right in pieces: edits.append( ' \u25e6 <i>%s</i> \u2192 <b>%s</b>' % (left, right)) if not edits: continue text = 'Updated:\n' + '\n'.join(edits) try: message = bot.send_message( chat_id=groupid, reply_to_message_id=lastmessage['message_id'], text=text, parse_mode='HTML', disable_web_page_preview=True, disable_notification=True) except ntelebot.errors.Error: logging.exception('While sending to %s:\n%s', groupid, text) continue lastnowdt = datetime.datetime.fromtimestamp(lastnow, tzinfo) preambles = groupconf['daily'].get('text', '').splitlines() preamble = preambles and preambles[lastnowdt.toordinal() % len(preambles)] or '' updated = 'Updated ' + humanize.time(nowdt) groupidnum = int(groupid) if -1002147483647 <= groupidnum < -1000000000000: updated = '<a href="https://t.me/c/%s/%s">%s</a>' % ( -1000000000000 - groupidnum, message['message_id'], updated) else: updated = '%s (%s)' % (updated, message['message_id']) text = '%s\n\n[%s]' % (_format_daily_message( preamble, list(map(form, events))), updated) try: if lastmessage.get('caption'): message = bot.edit_message_caption( chat_id=groupid, message_id=lastmessage['message_id'], caption=text, parse_mode='HTML') else: message = bot.edit_message_text( chat_id=groupid, message_id=lastmessage['message_id'], text=text, parse_mode='HTML', disable_web_page_preview=True) except ntelebot.errors.Error: logging.exception('While sending to %s:\n%s', groupid, text) else: records[key] = (lastnow, [event.copy() for event in events], message)
def inline(ctx, modconf): # pylint: disable=too-many-branches,too-many-locals """Handle @BOTNAME events.""" user_id = '%s' % ctx.user['id'] userconf = modconf['users'][user_id] calcodes = userconf.get('calendars') timezone = userconf.get('timezone') if not calcodes or not timezone: missing = [] if not calcodes: missing.append('choose one or more calendars') if not timezone: missing.append('set your time zone') return ctx.reply_inline([], is_personal=True, cache_time=30, switch_pm_text='Click to %s!' % humanize.list(missing), switch_pm_parameter='L2V2ZW50cw') calendar_view = ctx.bot.multibot.multical.view(calcodes.split()) tzinfo = pytz.timezone(timezone) terms = ctx.text.lower().split()[1:] full = False if terms and terms[0].lower() == 'full': terms.pop(0) full = True nextid = None results = [] while len(results) < 25: _, event, nextev = calendar_view.get_event(nextid) nextid = nextev and nextev['local_id'] if not event: break if full: text = ('%s %s' % (event['summary'], event['description'])).lower() else: text = event['summary'].lower() for term in terms: if term not in text: break else: subtitle = eventutil.humanize_range(event['start'], event['end'], tzinfo) if event['location']: subtitle = '%s @ %s' % (subtitle, event['location'].split(',', 1)[0]) if full and event['description']: title = '%s \u2022 %s' % (event['summary'], subtitle) description = html.sanitize(event['description'], strip=True) else: title = event['summary'] description = subtitle results.append({ 'description': description, 'input_message_content': { 'disable_web_page_preview': True, 'message_text': eventutil.format_event(ctx.bot, event, tzinfo, full=full), 'parse_mode': 'HTML', }, 'id': event['local_id'], #'thumb_url': icon, 'title': title, 'type': 'article', }) if not nextid: break ctx.reply_inline(results, is_personal=True, cache_time=30, switch_pm_text='Settings', switch_pm_parameter='L2V2ZW50cyBzZXQ')