示例#1
0
文件: clips.py 项目: Feriority/lrrbot
async def clips_vid(session, videoid):
    video = await get_video_data(videoid)

    clips = server.db.metadata.tables["clips"]
    with server.db.engine.begin() as conn:
        clip_data = conn.execute(
            sqlalchemy.select([
                clips.c.data, clips.c.time, clips.c.rating
            ]).where(clips.c.vodid == videoid.lstrip('v')).where(
                clips.c.deleted == False).order_by(
                    clips.c.time.asc())).fetchall()

    if video is None and clip_data:
        video = {'start': clip_data[0][1], 'title': 'Unknown video'}

    clip_data = [{
        "slug":
        clip['slug'],
        "title":
        clip['title'],
        "curator":
        clip['curator']['display_name'],
        "starttime":
        time - video['start'],
        "endtime":
        time - video['start'] + datetime.timedelta(seconds=clip['duration']),
        "start":
        nice_duration(time - video['start'], 0),
        "duration":
        nice_duration(clip['duration'], 0),
        "embed_html":
        clip['embed_html'],
        "game":
        clip['game'],
        "thumbnail":
        clip['thumbnails']['small'],
        "rating":
        rating,
        "overlap":
        False,
    } for clip, time, rating in clip_data]
    lastend = None
    prevclip = None
    for clip in clip_data:
        if lastend is not None and clip['starttime'] <= lastend:
            clip['overlap'] = True
        if lastend is None or lastend < clip['endtime']:
            lastend = clip['endtime']

    return flask.render_template("clips_vid.html",
                                 video=video,
                                 clips=clip_data,
                                 session=session)
    def get_chat_log(self, user):
        attachments = []
        now = datetime.datetime.now(config["timezone"])

        log = self.lrrbot.metadata.tables["log"]
        with self.lrrbot.engine.begin() as conn:
            rows = conn.execute(
                sqlalchemy.select(
                    [log.c.id, log.c.time,
                     log.c.message]).where(log.c.source == user.lower()).where(
                         log.c.time > now -
                         datetime.timedelta(days=1)).limit(3).order_by(
                             log.c.time.asc())).fetchall()

        logid = -1
        for logid, timestamp, message in rows:
            timestamp = timestamp.astimezone(config["timezone"])
            attachments.append({
                'text':
                slack.escape("%s (%s ago): %s" %
                             (timestamp.strftime("%H:%M"),
                              time.nice_duration(now - timestamp), message))
            })

        return logid, attachments
    def on_message(self, sender, message):
        action = message['data']['moderation_action']
        args = message['data']['args']
        mod = message['data']['created_by']

        if action == 'timeout':
            user = args[0]
            length = int(args[1])
            reason = args[2] if len(args) >= 3 else None
            logid, attachments = self.get_chat_log(user)
            same_user = (self.last_ban == (user.lower(), logid))
            if same_user:
                attachments = []
            self.last_ban = (user.lower(), logid)

            text = "%s was%s timed out for %s by %s." % (
                slack.escape(user), " also" if same_user else "",
                slack.escape(time.nice_duration(length, 0)), slack.escape(mod))
            if reason is not None:
                text += " Reason: %s" % slack.escape(reason)
        elif action == 'ban':
            user = args[0]
            reason = args[1] if len(args) >= 2 else None
            logid, attachments = self.get_chat_log(user)
            same_user = (self.last_ban == (user.lower(), logid))
            if same_user:
                attachments = []
            self.last_ban = (user.lower(), logid)

            text = "%s was%s banned by %s." % (slack.escape(user),
                                               " also" if same_user else "",
                                               slack.escape(mod))
            if reason is not None:
                text += " Reason: %s" % slack.escape(reason)
        elif action == 'unban':
            user = args[0]
            attachments = []
            self.last_ban = None

            text = "%s was unbanned by %s." % (slack.escape(user),
                                               slack.escape(mod))
        elif action == 'untimeout':
            user = args[0]
            attachments = []
            self.last_ban = None

            text = "%s was untimed-out by %s." % (slack.escape(user),
                                                  slack.escape(mod))
        else:
            log.info("Got unrecognised message: %r", message['data'])
            attachments = []
            self.last_ban = None

            text = "%s did a %s: %s" % (slack.escape(mod),
                                        slack.escape(action),
                                        slack.escape(repr(args)))

        asyncio. async (slack.send_message(text, attachments=attachments),
                        loop=self.loop).add_done_callback(
                            utils.check_exception)
示例#4
0
 def uptime_msg(self):
     stream_info = twitch.get_info()
     if stream_info and stream_info.get("stream_created_at"):
         start = dateutil.parser.parse(stream_info["stream_created_at"])
         now = datetime.datetime.now(datetime.timezone.utc)
         return "The stream has been live for %s." % time.nice_duration(
             now - start, 0)
     elif stream_info and stream_info.get('live'):
         return "Twitch won't tell me when the stream went live."
     else:
         return "The stream is not live."
示例#5
0
    def on_message(self, sender, message):
        log.info("Got message: %r", message['data'])

        action = message['data']['moderation_action']
        args = message['data']['args']
        mod = message['data']['created_by']

        if action == 'timeout':
            user = args[0]
            action = "Timeout: %s" % ctime.nice_duration(int(args[1]))
            reason = args[2] if len(args) >= 3 else ''
            last = self.last_chat.get(user.lower(), [''])[0]
        elif action == 'ban':
            user = args[0]
            action = "Ban"
            reason = args[1] if len(args) >= 2 else ''
            last = self.last_chat.get(user.lower(), [''])[0]
        elif action == 'unban':
            user = args[0]
            action = "Unban"
            reason = ''
            last = ''
        elif action == 'untimeout':
            user = args[0]
            action = "Untimeout"
            reason = ''
            last = ''
        elif action == 'delete':
            user = args[0]
            action = "Delete message"
            reason = ''
            last = args[1]
        else:
            user = ''
            reason = repr(args)
            last = ''

        now = datetime.datetime.now(config["timezone"])

        data = [
            ("Timestamp", now.strftime("%Y-%m-%d %H:%M:%S")),
            ("Timestamp (hours bussed)",
             self.nice_time(now - DESERTBUS_START)),
            ("Offender's Username", user),
            ("Moderator", mod),
            ("Enforcement option/length", action),
            ("What was the cause of the enforcement action?", reason),
            ("Last Line", last),
        ]
        log.debug("Add row: %r", data)
        asyncio.ensure_future(
            gdata.add_rows_to_spreadsheet(SPREADSHEET, [data]),
            loop=self.loop).add_done_callback(utils.check_exception)
示例#6
0
async def clips_vid(session, videoid):
	video = await get_video_data(videoid)

	clips = server.db.metadata.tables["clips"]
	with server.db.engine.begin() as conn:
		clip_data = conn.execute(
			sqlalchemy.select([clips.c.data, clips.c.time, clips.c.rating])
				.where(clips.c.vodid == videoid.lstrip('v'))
				.where(clips.c.deleted == False)
				.order_by(clips.c.time.asc())).fetchall()

	if video is None and clip_data:
		video = {'start': clip_data[0][1], 'title': 'Unknown video'}

	clip_data = [
		{
			"slug": clip['slug'],
			"title": clip['title'],
			"curator": clip['curator']['display_name'],
			"starttime": time - video['start'],
			"endtime": time - video['start'] + datetime.timedelta(seconds=clip['duration']),
			"start": nice_duration(time - video['start'], 0),
			"duration": nice_duration(clip['duration'], 0),
			"embed_html": clip['embed_html'],
			"game": clip['game'],
			"thumbnail": clip['thumbnails']['small'],
			"rating": rating,
			"overlap": False,
		}
		for clip, time, rating in clip_data
	]
	lastend = None
	prevclip = None
	for clip in clip_data:
		if lastend is not None and clip['starttime'] <= lastend:
			clip['overlap'] = True
		if lastend is None or lastend < clip['endtime']:
			lastend = clip['endtime']

	return flask.render_template("clips_vid.html", video=video, clips=clip_data, session=session)
	def on_message(self, sender, message):
		log.info("Got message: %r", message['data'])

		action = message['data']['moderation_action']
		args = message['data']['args']
		mod = message['data']['created_by']

		if action == 'timeout':
			user = args[0]
			action = "Timeout: %s" % ctime.nice_duration(int(args[1]))
			reason = args[2] if len(args) >= 3 else ''
			last = self.last_chat.get(user.lower(), [''])[0]
		elif action == 'ban':
			user = args[0]
			action = "Ban"
			reason = args[1] if len(args) >= 2 else ''
			last = self.last_chat.get(user.lower(), [''])[0]
		elif action == 'unban':
			user = args[0]
			action = "Unban"
			reason = ''
			last = ''
		elif action == 'untimeout':
			user = args[0]
			action = "Untimeout"
			reason = ''
			last = ''
		else:
			user = ''
			reason = repr(args)
			last = ''

		now = datetime.datetime.now(config["timezone"])

		data = [
			("Timestamp", now.strftime("%Y-%m-%d %H:%M:%S")),
			("Timestamp (hours bussed)", self.nice_time(now - DESERTBUS_START)),
			("Offender's Username", user),
			("Moderator", mod),
			("Enforcement option/length", action),
			("What was the cause of the enforcement action?", reason),
			("Last Line", last),
		]
		log.debug("Add row: %r", data)
		asyncio.ensure_future(gdata.add_rows_to_spreadsheet(SPREADSHEET, [data]), loop=self.loop).add_done_callback(utils.check_exception)
示例#8
0
	def get_chat_log(self, user):
		attachments = []
		now = datetime.datetime.now(config["timezone"])

		log = self.lrrbot.metadata.tables["log"]
		with self.lrrbot.engine.begin() as conn:
			rows = conn.execute(sqlalchemy.select([log.c.id, log.c.time, log.c.message])
				.where(log.c.source == user.lower())
				.where(log.c.time > now - datetime.timedelta(days=1))
				.limit(3)
				.order_by(log.c.time.desc())).fetchall()

		logid = -1
		for logid, timestamp, message in rows[::-1]:
			timestamp = timestamp.astimezone(config["timezone"])
			attachments.append({
				'text': slack.escape("%s (%s ago): %s" % (timestamp.strftime("%H:%M"), time.nice_duration(now - timestamp), message))
			})

		return logid, attachments
示例#9
0
文件: utils.py 项目: mrphlip/lrrbot
def timestamp(ts, cls='timestamp', tag='span'):
	"""
	Outputs a given time (either unix timestamp or datetime instance) as a human-readable time
	and includes tags so that common.js will convert the time on page-load to the user's
	timezone and preferred date/time format.
	"""
	if isinstance(ts, (int, float)):
		ts = datetime.datetime.fromtimestamp(ts, tz=pytz.utc)
	elif ts.tzinfo is None:
		ts = ts.replace(tzinfo=datetime.timezone.utc)
	ts = ts.astimezone(config.config['timezone'])
	if cls == 'timestamp-duration':
		text = nice_duration(datetime.datetime.now(config.config['timezone']) - ts, 2)
	else:
		text = ts.strftime("%A, %d %B, %Y %H:%M:%S %Z")
	return flask.Markup("<{tag} class=\"{cls}\" data-timestamp=\"{timestamp}\">{text}</{tag}>".format(
		text=text,
		timestamp=ts.timestamp(),
		tag=tag,
		cls=cls,
	))
示例#10
0
文件: utils.py 项目: d3fr0st/lrrbot
def timestamp(ts, cls='timestamp', tag='span'):
    """
	Outputs a given time (either unix timestamp or datetime instance) as a human-readable time
	and includes tags so that common.js will convert the time on page-load to the user's
	timezone and preferred date/time format.
	"""
    if isinstance(ts, (int, float)):
        ts = datetime.datetime.fromtimestamp(ts, tz=pytz.utc)
    elif ts.tzinfo is None:
        ts = ts.replace(tzinfo=datetime.timezone.utc)
    ts = ts.astimezone(config.config['timezone'])
    if cls == 'timestamp-duration':
        text = nice_duration(
            datetime.datetime.now(config.config['timezone']) - ts, 2)
    else:
        text = ts.strftime("%A, %d %B, %Y %H:%M:%S %Z")
    return flask.Markup(
        "<{tag} class=\"{cls}\" data-timestamp=\"{timestamp}\">{text}</{tag}>".
        format(
            text=text,
            timestamp=ts.timestamp(),
            tag=tag,
            cls=cls,
        ))
示例#11
0
    async def update_topic(self):
        channel = self.eris.get_server(
            config['discord_serverid']).default_channel
        header = await rpc.bot.get_header_info()
        messages = []

        if header['is_live']:
            shows = self.metadata.tables["shows"]
            games = self.metadata.tables["games"]
            game_per_show_data = self.metadata.tables["game_per_show_data"]
            with self.engine.begin() as conn:
                if header.get('current_game'):
                    game = conn.execute(
                        sqlalchemy.select([
                            sqlalchemy.func.coalesce(
                                game_per_show_data.c.display_name,
                                games.c.name)
                        ]).select_from(
                            games.outerjoin(
                                game_per_show_data,
                                (game_per_show_data.c.game_id == games.c.id) &
                                (game_per_show_data.c.show_id
                                 == header['current_show']['id']))).where(
                                     games.c.id == header['current_game']
                                     ['id'])).first()
                    if game is not None:
                        game, = game
                else:
                    game = None

                if header.get('current_show'):
                    show = conn.execute(
                        sqlalchemy.select([shows.c.name]).where(
                            shows.c.id == header['current_show']['id']).where(
                                shows.c.string_id != "")).first()
                    if show is not None:
                        show, = show
                else:
                    show = None

            if game and show:
                messages.append("Now live: %s on %s." % (game, show))
            elif game:
                messages.append("Now live: %s." % game)
            elif show:
                messages.append("Now live: %s." % show)
            messages.append(self.uptime_msg())
        else:
            now = datetime.datetime.now(datetime.timezone.utc)
            events = googlecalendar.get_next_event(googlecalendar.CALENDAR_LRL,
                                                   after=now)
            for event in events:
                if event['start'] > now:
                    message = "In %s: " % time.nice_duration(
                        event['start'] - now, 1)
                else:
                    message = "%s ago: " % time.nice_duration(
                        now - event['start'], 1)
                message += event['title']
                if event['description'] is not None:
                    message += " (%s)" % (textwrap.shorten(
                        googlecalendar.process_description(
                            event['description']), 200))
                message += " at " + event['start'].astimezone(
                    config['timezone']).strftime(googlecalendar.DISPLAY_FORMAT)
                message += "."
                messages.append(message)
        if header.get('advice'):
            messages.append(header['advice'])
        await self.eris.edit_channel(channel,
                                     topic=textwrap.shorten(
                                         " ".join(messages), MAX_TOPIC_LENGTH))
示例#12
0
	def on_message(self, sender, message):
		log.info("Got message: %r", message['data'])

		action = message['data']['moderation_action']
		args = message['data']['args']
		mod = message['data']['created_by']

		if action in ('timeout', 'ban'):
			user = args[0]
			logid, attachments = self.get_chat_log(user)
			same_user = (self.last_ban == (user.lower(), logid))
			if same_user:
				attachments = []
			self.last_ban = (user.lower(), logid)
		else:
			attachments = []
			self.last_ban = None

		if action == 'timeout':
			user = args[0]
			length = time.nice_duration(int(args[1]), 0) if args[1] != '' else '???'
			reason = args[2] if len(args) >= 3 else None
			text = "%s was%s timed out for %s by %s." % (slack.escape(user), " also" if same_user else "", slack.escape(length), slack.escape(mod))
			if reason is not None:
				text += " Reason: %s" % slack.escape(reason)
		elif action == 'ban':
			user = args[0]
			reason = args[1] if len(args) >= 2 else None
			text = "%s was%s banned by %s." % (slack.escape(user), " also" if same_user else "", slack.escape(mod))
			if reason is not None:
				text += " Reason: %s" % slack.escape(reason)
		elif action == 'unban':
			user = args[0]
			text = "%s was unbanned by %s." % (slack.escape(user), slack.escape(mod))
		elif action == 'untimeout':
			user = args[0]
			text = "%s was untimed-out by %s." % (slack.escape(user), slack.escape(mod))

		elif action in ('twitchbot_rejected', 'automod_rejected'):
			msg_id = message['data']['msg_id']
			user = args[0]
			message = args[1]
			if not mod:
				mod = "the strange voices that lie beneath"
			text = "%s's message was rejected by %s." % (slack.escape(user), slack.escape(mod))
			attachments.append({
				'text': slack.escape(message)
			})

			# Approve the message because we're unable to turn off Automod.
			if config['autoautomod']:
				asyncio.ensure_future(twitch.twitchbot_approve(msg_id), loop=self.loop).add_done_callback(utils.check_exception)
		elif action in ('approved_twitchbot_message', 'approved_automod_message'):
			user = args[0]
			text = "%s approved %s's message." % (slack.escape(mod), slack.escape(user))
		elif action in ('denied_twitchbot_message', 'denied_automod_message'):
			user = args[0]
			text = "%s denied %s's message." % (slack.escape(mod), slack.escape(user))

		elif action == 'slow':
			duration = int(args[0])
			text = "%s has enabled slow mode: delay %s." % (slack.escape(mod), slack.escape(time.nice_duration(duration, 0)))
		elif action == 'slowoff':
			text = "%s has disabled slow mode." % (slack.escape(mod), )
		elif action == 'followers':
			duration = int(args[0])
			text = "%s has enabled follower-only mode: minimum age %s." % (slack.escape(mod), slack.escape(time.nice_duration(duration, 0)))
		elif action == 'followersoff':
			text = "%s has disabled follower-only mode." % (slack.escape(mod), )

		elif action == 'host':
			target = args[0]
			text = "%s has enabled hosting of %s." % (slack.escape(mod), slack.escape(target))
		elif action == 'unhost':
			text = "%s has disabled hosting." % (slack.escape(mod), )

		elif action == 'mod':
			target = args[0]
			text = "%s has made %s a moderator." % (slack.escape(mod), slack.escape(target))
		elif action == 'clear':
			text = "%s cleared the chat." % (slack.escape(mod), )
		elif action == 'recent_cheer_dismissal':
			cheerer = args[0]
			text = "%s has cleared %s's recent-cheer notice." % (slack.escape(mod), slack.escape(cheerer))

		else:
			text = "%s did a %s: %s" % (slack.escape(mod), slack.escape(action), slack.escape(repr(args)))

		asyncio.ensure_future(slack.send_message(text, attachments=attachments), loop=self.loop).add_done_callback(utils.check_exception)
示例#13
0
    def on_message(self, sender, message):
        log.info("Got message: %r", message['data'])

        action = message['data']['moderation_action']
        args = message['data']['args']
        mod = message['data']['created_by']

        if action in ('timeout', 'ban'):
            user = args[0]
            logid, attachments = self.get_chat_log(user)
            same_user = (self.last_ban == (user.lower(), logid))
            if same_user:
                attachments = []
            self.last_ban = (user.lower(), logid)
        else:
            attachments = []
            self.last_ban = None

        if action == 'timeout':
            user = args[0]
            length = int(args[1])
            reason = args[2] if len(args) >= 3 else None
            text = "%s was%s timed out for %s by %s." % (
                slack.escape(user), " also" if same_user else "",
                slack.escape(time.nice_duration(length, 0)), slack.escape(mod))
            if reason is not None:
                text += " Reason: %s" % slack.escape(reason)
        elif action == 'ban':
            user = args[0]
            reason = args[1] if len(args) >= 2 else None
            text = "%s was%s banned by %s." % (slack.escape(user),
                                               " also" if same_user else "",
                                               slack.escape(mod))
            if reason is not None:
                text += " Reason: %s" % slack.escape(reason)
        elif action == 'unban':
            user = args[0]
            text = "%s was unbanned by %s." % (slack.escape(user),
                                               slack.escape(mod))
        elif action == 'untimeout':
            user = args[0]
            text = "%s was untimed-out by %s." % (slack.escape(user),
                                                  slack.escape(mod))

        elif action == 'twitchbot_rejected':
            user = args[0]
            message = args[1]
            # mod is always "twitchbot", but still...
            text = "%s's message was rejected by %s." % (slack.escape(user),
                                                         slack.escape(mod))
            attachments.append({'text': slack.escape(message)})
        elif action == 'approved_twitchbot_message':
            user = args[0]
            text = "%s approved %s's message." % (slack.escape(mod),
                                                  slack.escape(user))
        elif action == 'denied_twitchbot_message':
            user = args[0]
            text = "%s denied %s's message." % (slack.escape(mod),
                                                slack.escape(user))

        elif action == 'slow':
            duration = int(args[0])
            text = "%s has enabled slow mode: delay %s." % (slack.escape(
                mod), slack.escape(time.nice_duration(duration, 0)))
        elif action == 'slowoff':
            text = "%s has disabled slow mode." % (slack.escape(mod), )
        elif action == 'followers':
            duration = int(args[0])
            text = "%s has enabled follower-only mode: minimum age %s." % (
                slack.escape(mod), slack.escape(time.nice_duration(
                    duration, 0)))
        elif action == 'followersoff':
            text = "%s has disabled follower-only mode." % (
                slack.escape(mod), )

        elif action == 'host':
            target = args[0]
            text = "%s has enabled hosting of %s." % (slack.escape(mod),
                                                      slack.escape(target))
        elif action == 'unhost':
            text = "%s has disabled hosting." % (slack.escape(mod), )

        elif action == 'mod':
            target = args[0]
            text = "%s has made %s a moderator." % (slack.escape(mod),
                                                    slack.escape(target))
        elif action == 'clear':
            text = "%s cleared the chat." % (slack.escape(mod), )
        elif action == 'recent_cheer_dismissal':
            cheerer = args[0]
            text = "%s has cleared %s's recent-cheer notice." % (
                slack.escape(mod), slack.escape(cheerer))

        else:
            text = "%s did a %s: %s" % (slack.escape(mod),
                                        slack.escape(action),
                                        slack.escape(repr(args)))

        asyncio. async (slack.send_message(text, attachments=attachments),
                        loop=self.loop).add_done_callback(
                            utils.check_exception)
示例#14
0
	def on_message(self, sender, message):
		log.info("Got message: %r", message['data'])

		action = message['data']['moderation_action']
		args = message['data']['args']
		mod = message['data']['created_by']

		if action in ('timeout', 'ban'):
			user = args[0]
			logid, attachments = self.get_chat_log(user)
			same_user = (self.last_ban == (user.lower(), logid))
			if same_user:
				attachments = []
			self.last_ban = (user.lower(), logid)
		else:
			attachments = []
			self.last_ban = None

		if action == 'timeout':
			user = args[0]
			length = time.nice_duration(int(args[1]), 0) if args[1] != '' else '???'
			reason = args[2] if len(args) >= 3 else None
			text = "%s was%s timed out for %s by %s." % (slack.escape(user), " also" if same_user else "", slack.escape(length), slack.escape(mod))
			if reason is not None:
				text += " Reason: %s" % slack.escape(reason)
		elif action == 'ban':
			user = args[0]
			reason = args[1] if len(args) >= 2 else None
			text = "%s was%s banned by %s." % (slack.escape(user), " also" if same_user else "", slack.escape(mod))
			if reason is not None:
				text += " Reason: %s" % slack.escape(reason)
		elif action == 'unban':
			user = args[0]
			text = "%s was unbanned by %s." % (slack.escape(user), slack.escape(mod))
		elif action == 'untimeout':
			user = args[0]
			text = "%s was untimed-out by %s." % (slack.escape(user), slack.escape(mod))
		elif action == 'delete':
			user = args[0]
			message = args[1]
			text = "%s had a message deleted by %s." % (slack.escape(user), slack.escape(mod))
			attachments.append({
				'text': slack.escape(message)
			})

		elif action in ('twitchbot_rejected', 'automod_rejected'):
			msg_id = message['data']['msg_id']
			user = args[0]
			message = args[1]
			if not mod:
				mod = "the strange voices that lie beneath"
			text = "%s's message was rejected by %s." % (slack.escape(user), slack.escape(mod))
			attachments.append({
				'text': slack.escape(message)
			})

			# Approve the message because we're unable to turn off Automod.
			if config['autoautomod']:
				asyncio.ensure_future(twitch.twitchbot_approve(msg_id), loop=self.loop).add_done_callback(utils.check_exception)
		elif action in ('approved_twitchbot_message', 'approved_automod_message'):
			user = args[0]
			text = "%s approved %s's message." % (slack.escape(mod), slack.escape(user))
		elif action in ('denied_twitchbot_message', 'denied_automod_message'):
			user = args[0]
			text = "%s denied %s's message." % (slack.escape(mod), slack.escape(user))

		elif action == 'slow':
			duration = int(args[0])
			text = "%s has enabled slow mode: delay %s." % (slack.escape(mod), slack.escape(time.nice_duration(duration, 0)))
		elif action == 'slowoff':
			text = "%s has disabled slow mode." % (slack.escape(mod), )
		elif action == 'followers':
			duration = int(args[0])
			text = "%s has enabled follower-only mode: minimum age %s." % (slack.escape(mod), slack.escape(time.nice_duration(duration, 0)))
		elif action == 'followersoff':
			text = "%s has disabled follower-only mode." % (slack.escape(mod), )

		elif action == 'host':
			target = args[0]
			text = "%s has enabled hosting of %s." % (slack.escape(mod), slack.escape(target))
		elif action == 'unhost':
			text = "%s has disabled hosting." % (slack.escape(mod), )

		elif action == 'mod':
			target = args[0]
			text = "%s has made %s a moderator." % (slack.escape(mod), slack.escape(target))
		elif action == 'clear':
			text = "%s cleared the chat." % (slack.escape(mod), )
		elif action == 'recent_cheer_dismissal':
			cheerer = args[0]
			text = "%s has cleared %s's recent-cheer notice." % (slack.escape(mod), slack.escape(cheerer))

		else:
			text = "%s did a %s: %s" % (slack.escape(mod), slack.escape(action), slack.escape(repr(args)))

		asyncio.ensure_future(slack.send_message(text, attachments=attachments), loop=self.loop).add_done_callback(utils.check_exception)