Esempio n. 1
0
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))
Esempio n. 2
0
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']
Esempio n. 3
0
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)
Esempio n. 4
0
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)
Esempio n. 5
0
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')