import bot
import cat
+class Announcement(object):
+ def __init__(self, row):
+ for k, v in dict(row).items():
+ setattr(self, k, v)
+
class Announcements(DBConnection):
def __init__(self, filename = None):
super(Announcements, self).__init__(filename)
# Delete old announcements.
now = int(time.time())
for announcement in self.get_all_announcements(client):
- if announcement['asap'] == 'true':
+ if announcement.asap == 'true':
# Don't delete announcements that were requested ASAP.
continue
- if not announcement['last_spoke']:
+ if not announcement.last_spoke:
# Don't delete announcements that haven't played yet.
continue
- interval = announcement['interval']
+ interval = announcement.interval
if not interval:
interval = 0
- if now - announcement['last_spoke'] < max(interval, 86400):
+ if now - announcement.last_spoke < max(interval, 86400):
# Don't delete announcements that aren't old.
continue
- if announcement['interval']:
- if not announcement['end_date']:
+ if announcement.interval:
+ if not announcement.end_date:
# Don't delete ongoing announcements.
continue
- if announcement['end_date'] > now:
+ if announcement.end_date > now:
# Don't delete announcements that still have time to run.
continue
if bot.get('dryrun'):
- log.info("Would delete old announcement {}".format(announcement['id']))
+ log.info("Would delete old announcement {}".format(announcement.id))
else:
- if self.delete_announcement(client, announcement['id']):
- log.info('Deleted old announcement {}'.format(announcement['id']))
+ if self.delete_announcement(client, announcement.id):
+ log.info('Deleted old announcement {}'.format(announcement.id))
else:
- log.info('Failed to delete old announcement {}'.format(announcement['id']))
+ log.info('Failed to delete old announcement {}'.format(announcement.id))
yield from asyncio.sleep(waittime)
yield from self.help_announcements(message, *args)
def get_all_announcements(self, client, servers = []):
- yield from self.get_all_from_table(client, 'announcements', servers)
+ for row in self.get_all_from_table(client, 'announcements', servers):
+ yield Announcement(row)
def get_announcement(self, client, id):
- return self.get_from_table(client, 'announcements', id)
+ row = self.get_from_table(client, 'announcements', id)
+ return Announcement(row) if row else None
def get_announcements(self, client):
now = self.now()
cursor = self.query("select id, server_id, member_id, channel_id, voice_id, sound, attachment, mention, start_date, end_date, interval, probability, last_spoke, asap, message, digest from announcements where (start_date is null or start_date <= ?) and (end_date is null or end_date > ? or end_date=start_date) and (asap='true' or last_spoke is null or last_spoke < ? - interval) and (last_spoke is null or interval > 0) and bot_id=?", [now, now, now, client.user.id])
for row in cursor.fetchall():
- yield dict(row)
+ yield Announcement(row)
self.close_db()
def create_announcement(self, client, **args):
# Anyone on the server can show details of an announcement.
if command == 'show':
if announcement is not None:
- if announcement['member_id'] == author.id:
+ if announcement.member_id == author.id:
log.debug('Anyone can show own announcements.')
return announcement
- if announcement['channel_id'] != 'private':
- server = client.get_server(announcement['server_id'])
+ if announcement.channel_id != 'private':
+ server = client.get_server(announcement.server_id)
if server is not None:
member = server.get_member(author.id)
if member in server.members:
- log.debug('Member {} on server {} can show announcement {}.'.format(member.name, server.name, announcement['id']))
+ log.debug('Member {} on server {} can show announcement {}.'.format(member.name, server.name, announcement.id))
return announcement
if command in ['edit', 'delete', 'schedule']:
if announcement is not None:
- if announcement['member_id'] == author.id:
+ if announcement.member_id == author.id:
log.debug('Anyone can manage own announcements.')
return announcement
- if announcement['channel_id'] != 'private':
- server = client.get_server(announcement['server_id'])
- announcer = server.get_member(announcement['member_id'])
+ if announcement.channel_id != 'private':
+ server = client.get_server(announcement.server_id)
+ announcer = server.get_member(announcement.member_id)
announcer_role = bot.highest_role(announcer.roles)
member = server.get_member(author.id)
member_role = bot.highest_role(member.roles)
return announcement
if command == 'create':
- if announcement['channel_id'] == 'private':
+ if announcement.channel_id == 'private':
log.debug('Anyone can schedule a private announcement.')
return announcement
- server = client.get_server(announcement['server_id'])
- member = server.get_member(announcement['member_id'])
+ server = client.get_server(announcement.server_id)
+ member = server.get_member(announcement.member_id)
bot_member = server.get_member(client.user.id)
member_role = bot.highest_role(member.roles)
bot_role = bot.highest_role(bot_member.roles)
results = []
for announcement in self.get_all_announcements(client, self.get_message_servers(client, message, True)):
- if announcement['channel_id'] == 'private' and announcement['member_id'] != message.author.id:
+ if announcement.channel_id == 'private' and announcement.member_id != message.author.id:
continue
- text = 'announcement **{}**'.format(announcement['id'])
- if announcement['channel_id'] == 'private':
+ text = 'announcement **{}**'.format(announcement.id)
+ if announcement.channel_id == 'private':
text += ' in private'
else:
- server = client.get_server(announcement['server_id'])
- member = server.get_member(announcement['member_id'])
+ server = client.get_server(announcement.server_id)
+ member = server.get_member(announcement.member_id)
text += ' by {}'.format(member.name)
- if announcement['channel_id']:
- if announcement['channel_id'] != 'private':
- text += ' in <#{}>'.format(announcement['channel_id'])
- if announcement['voice_id']:
- if announcement['sound']:
- text += ' sound {}'.format(announcement['sound'])
- if announcement['voice_id'] != announcement['channel_id']:
- text += ' in <#{}>'.format(announcement['voice_id'])
- if announcement['attachment']:
- text += ' attachment {}'.format(announcement['attachment'])
- if announcement['message']:
- short = announcement['message'][:100]
- if short != announcement['message']:
+ if announcement.channel_id:
+ if announcement.channel_id != 'private':
+ text += ' in <#{}>'.format(announcement.channel_id)
+ if announcement.voice_id:
+ if announcement.sound:
+ text += ' sound {}'.format(announcement.sound)
+ if announcement.voice_id != announcement.channel_id:
+ text += ' in <#{}>'.format(announcement.voice_id)
+ if announcement.attachment:
+ text += ' attachment {}'.format(announcement.attachment)
+ if announcement.message:
+ short = announcement.message[:100]
+ if short != announcement.message:
short += '...'
text += ': {}'.format(short.replace('\n', ' '))
results.append(text)
return
lines = []
- if announcement['channel_id'] == 'private':
+ if announcement.channel_id == 'private':
for server in client.servers:
try:
- member = server.get_member(announcement['member_id'])
+ member = server.get_member(announcement.member_id)
except:
log.exception("announce")
else:
- server = client.get_server(announcement['server_id'])
- member = server.get_member(announcement['member_id'])
- text = '**{}announcement {} by {}'.format('private ' if announcement['channel_id'] == 'private' else '', id, member.name if member is not None else announcement['member_id'])
+ server = client.get_server(announcement.server_id)
+ member = server.get_member(announcement.member_id)
+ text = '**{}announcement {} by {}'.format('private ' if announcement.channel_id == 'private' else '', id, member.name if member is not None else announcement.member_id)
now = int(time.time())
- if announcement['last_spoke']:
- offset = announcement['last_spoke']
- text += ' last given at {}'.format(bot.iso8601(announcement['last_spoke']))
+ if announcement.last_spoke:
+ offset = announcement.last_spoke
+ text += ' last given at {}'.format(bot.iso8601(announcement.last_spoke))
else:
offset = now
- if announcement['start_date']:
- if announcement['start_date'] > now:
- offset = announcement['start_date']
- text += ' scheduled for {}'.format(bot.iso8601(announcement['start_date']))
- if announcement['end_date']:
- if announcement['end_date'] < now:
+ if announcement.start_date:
+ if announcement.start_date > now:
+ offset = announcement.start_date
+ text += ' scheduled for {}'.format(bot.iso8601(announcement.start_date))
+ if announcement.end_date:
+ if announcement.end_date < now:
offset = None
text += ' has expired'
if offset is not None and offset <= now:
- if announcement['interval']:
- text += ' scheduled for {}'.format(bot.iso8601(offset + announcement['interval']))
- if announcement['asap'] == 'true':
+ if announcement.interval:
+ text += ' scheduled for {}'.format(bot.iso8601(offset + announcement.interval))
+ if announcement.asap == 'true':
text += ' will be given ASAP'
- if announcement['probability'] < 0:
+ if announcement.probability < 0:
text += ' paused'
text += '**'
lines.append(text)
text = 'announce create'
- if announcement['start_date']:
- text += ' from "{}"'.format(bot.iso8601(announcement['start_date']))
- if announcement['end_date']:
- text += ' to "{}"'.format(bot.iso8601(announcement['end_date']))
- if announcement['interval']:
- text += ' every {}'.format(bot.unparse_seconds(int(announcement['interval'])))
- mention = announcement['mention']
+ if announcement.start_date:
+ text += ' from "{}"'.format(bot.iso8601(announcement.start_date))
+ if announcement.end_date:
+ text += ' to "{}"'.format(bot.iso8601(announcement.end_date))
+ if announcement.interval:
+ text += ' every {}'.format(bot.unparse_seconds(int(announcement.interval)))
+ mention = announcement.mention
if mention:
if mention == 'everyone' or mention == 'here':
text += ' tell @{}'.format(mention)
text += ' tell @{}'.format(mention)
else:
text += ' tell <@{}>'.format(mention)
- if announcement['channel_id'] != announcement['voice_id']:
- if announcement['channel_id'] == 'private':
+ if announcement.channel_id != announcement.voice_id:
+ if announcement.channel_id == 'private':
text += ' in private'
else:
- text += ' in <#{}>'.format(announcement['channel_id'])
- if announcement['voice_id']:
- text += ' voice <#{}>'.format(announcement['voice_id'])
- if announcement['sound']:
- text += ' sound {}'.format(announcement['sound'])
- if announcement['attachment']:
- text += ' attachment {}'.format(announcement['attachment'])
- if abs(announcement['probability']) < 1.0:
- text += ' probability {}'.format(abs(announcement['probability']))
-
- if announcement['message']:
+ text += ' in <#{}>'.format(announcement.channel_id)
+ if announcement.voice_id:
+ text += ' voice <#{}>'.format(announcement.voice_id)
+ if announcement.sound:
+ text += ' sound {}'.format(announcement.sound)
+ if announcement.attachment:
+ text += ' attachment {}'.format(announcement.attachment)
+ if abs(announcement.probability) < 1.0:
+ text += ' probability {}'.format(abs(announcement.probability))
+
+ if announcement.message:
text += ':'
lines.append(text)
- if announcement['message']:
- lines.append(announcement['message'])
+ if announcement.message:
+ lines.append(announcement.message)
yield from bot.say_many(message.channel, lines)
if create is None:
return
- announce = yield from self.can_manage_announcements(message.author, message.channel, 'create', create = create)
+ announce = yield from self.can_manage_announcements(message.author, message.channel, 'create', create = Announcement(create))
if not announce:
return
# Pause announcement.
if 'pause' in args:
if args['pause']:
- update['probability'] = -abs(announcement['probability'])
+ update['probability'] = -abs(announcement.probability)
else:
- update['probability'] = abs(announcement['probability'])
+ update['probability'] = abs(announcement.probability)
# ASAP.
if 'asap' in args:
@asyncio.coroutine
def announce(self, announcement):
update = {}
- interval = announcement['interval']
+ interval = announcement.interval
if interval is None:
interval = 0
# Adhere to schedule.
- if bot.parse_boolean(announcement['asap']):
- log.info('Announcement {} was requested ASAP'.format(announcement['id']))
- announcement['probability'] = 1.0
+ if bot.parse_boolean(announcement.asap):
+ log.info('Announcement {} was requested ASAP'.format(announcement.id))
+ announcement.probability = 1.0
update['asap'] = 'false'
elif interval:
- if announcement['start_date'] or announcement['last_spoke']:
+ if announcement.start_date or announcement.last_spoke:
now = int(time.time())
- if announcement['start_date']:
- start = announcement['start_date']
+ if announcement.start_date:
+ start = announcement.start_date
else:
- start = announcement['last_spoke']
+ start = announcement.last_spoke
intervals = math.floor((now - start) / interval)
if intervals:
# We missed a schedule.
scheduled = start + intervals * interval
if now - scheduled > max(60, interval / 2):
- log.info('Announcement {} should have been given at {}'.format(announcement['id'], bot.iso8601(scheduled)))
- if 'digest' in announcement:
- update['digest'] = announcement['digest']
+ log.info('Announcement {} should have been given at {}'.format(announcement.id, bot.iso8601(scheduled)))
+ if announcement.digest:
+ update['digest'] = announcement.digest
update['last_spoke'] = scheduled
- self.update_announcement(client, announcement['id'], **update)
+ self.update_announcement(client, announcement.id, **update)
return
# Give the announcement now but set the original schedule.
last_spoke = start + (intervals + 1) * interval
if last_spoke < now:
update['last_spoke'] = last_spoke
- log.info('Forcing last_spoke for announcement {} to {} in line with schedule'.format(announcement['id'], bot.iso8601(update['last_spoke'])))
+ log.info('Forcing last_spoke for announcement {} to {} in line with schedule'.format(announcement.id, bot.iso8601(update['last_spoke'])))
if 'asap' not in update and 'last_spoke' not in update:
update['last_spoke'] = int(time.time())
channel = None
- channel_id = str(announcement['channel_id'])
+ channel_id = str(announcement.channel_id)
private = channel_id == 'private'
if private:
for server in client.servers:
try:
- member = server.get_member(announcement['member_id'])
+ member = server.get_member(announcement.member_id)
channel = yield from client.start_private_message(member)
break
except:
else:
channel = client.get_channel(channel_id)
if channel is None:
- log.warning("Can't get channel for announcement {}".format(announcement['id']))
+ log.warning("Can't get channel for announcement {}".format(announcement.id))
# Set last_spoke so we don't spam.
- self.update_announcement(client, announcement['id'], **update)
+ self.update_announcement(client, announcement.id, **update)
return
attachment = None
- if announcement['attachment']:
- attachment = bot.parse_attachment(announcement['attachment'])
+ if announcement.attachment:
+ attachment = bot.parse_attachment(announcement.attachment)
- voice_only = attachment is None and announcement['voice_id'] == announcement['channel_id']
- log.debug('Announcement {} {}.'.format(announcement['id'], 'is voice only' if voice_only else 'has text'))
+ voice_only = attachment is None and announcement.voice_id == announcement.channel_id
+ log.debug('Announcement {} {}.'.format(announcement.id, 'is voice only' if voice_only else 'has text'))
already_posted = False
if voice_only:
text = None
else:
# @everyone mention mangles the digest
- text = announcement['message']
- mention = announcement['mention']
+ text = announcement.message
+ mention = announcement.mention
if mention:
if mention in ['everyone', 'here']:
text = '@{}\n{}'.format(mention, text)
else:
text = '<@{}>\n{}'.format(mention, text)
- digest = announcement['digest']
+ digest = announcement.digest
# Always post private messages. Try not to spam public announcements.
- if not private and not bot.parse_boolean(announcement['asap']):
+ if not private and not bot.parse_boolean(announcement.asap):
cutoff = datetime.datetime.utcnow() - datetime.timedelta(0, interval)
log.debug('Getting logs from {}.'.format(cutoff))
result = yield from client.logs_from(channel, after = cutoff)
for message in logs:
raw = re.sub(r'^(<?@\S+\s)+', '', message.content).strip()
if digest in [bot.digest(message.content), bot.digest(raw)]:
- log.debug('Saw previously posted announcement {} with digest {}'.format(announcement['id'], digest))
+ log.debug('Saw previously posted announcement {} with digest {}'.format(announcement.id, digest))
already_posted = True
break
- if announcement['voice_id']:
- filename = announcement['sound']
+ if announcement.voice_id:
+ filename = announcement.sound
if not filename:
filename = 'meow.wav'
- log.debug('Will play {} for announcement {}.'.format(filename, announcement['id']))
+ log.debug('Will play {} for announcement {}.'.format(filename, announcement.id))
try:
logtext = text
if bot.get('dryrun'):
if not already_posted:
if voice_only:
- log.info('Dryrun: Not playing announcement {} {} in {}'.format(announcement['id'], filename, channel))
+ log.info('Dryrun: Not playing announcement {} {} in {}'.format(announcement.id, filename, channel))
else:
- log.info('Dryrun: Not posting announcement {} to {}: {}'.format(announcement['id'], channel, logtext))
+ log.info('Dryrun: Not posting announcement {} to {}: {}'.format(announcement.id, channel, logtext))
else:
announce = False
if not already_posted:
- if random.random() <= announcement['probability']:
+ if random.random() <= announcement.probability:
announce = True
if announce:
if voice_only:
yield from bot.wake_up()
else:
- log.info('Posting announcement {} to {}: {}'.format(announcement['id'], channel, logtext))
+ log.info('Posting announcement {} to {}: {}'.format(announcement.id, channel, logtext))
message = yield from bot.say(channel, text, attachment = attachment)
update['digest'] = bot.digest(message.content)
else:
- if announcement['probability'] < 0:
- log.info("Announcement {} is paused".format(announcement['id']))
+ if announcement.probability < 0:
+ log.info("Announcement {} is paused".format(announcement.id))
else:
- log.info("Didn't bother with announcement {}".format(announcement['id']))
+ log.info("Didn't bother with announcement {}".format(announcement.id))
# Set last_spoke even if we chose not to announce.
- self.update_announcement(client, announcement['id'], **update)
+ self.update_announcement(client, announcement.id, **update)
if not announce:
return
- if announcement['voice_id']:
- log.info('Playing announcement {} {} in {}'.format(announcement['id'], filename, channel))
- yield from bot.play_sound(client.get_channel(str(announcement['voice_id'])), filename)
+ if announcement.voice_id:
+ log.info('Playing announcement {} {} in {}'.format(announcement.id, filename, channel))
+ yield from bot.play_sound(client.get_channel(str(announcement.voice_id)), filename)
except:
log.exception("announce")