if 'id' in args:
announcement = db.get_announcement(client, args['id'])
+ elif 'create' in args:
+ announcement = args['create']
else:
announcement = None
# Anyone on the server can show details of an announcement.
if command == 'show':
if announcement is not None:
- server = client.get_server(announcement['server_id'])
- if server is not None:
- if member in server.members:
- log.debug('Member {} on server {} can show announcement {}.'.format(member.name, server.name, announcement['id']))
- return announcement
+ if announcement['member_id'] == member.id:
+ log.debug('Anyone can show own announcements.')
+ return announcement
+ if announcement['channel_id'] != 'private':
+ server = client.get_server(announcement['server_id'])
+ if server is not None:
+ if member in server.members:
+ log.debug('Member {} on server {} can show announcement {}.'.format(member.name, server.name, announcement['id']))
+ return announcement
if command in ['delete', 'schedule']:
if announcement is not None:
- server = client.get_server(announcement['server_id'])
- announcer = server.get_member(announcement['member_id'])
- if member.id == announcer.id:
- log.debug('Member {} on server {} can edit own announcements.'.format(member.name, server.name))
- return announcement
- announcer_role = highest_role(announcer.roles)
- member_role = highest_role(member.roles)
- if member_role > announcer_role:
- log.debug('Member {} on server {} can edit announcements from lower role.'.format(member.name, server.name))
+ if announcement['member_id'] == member.id:
+ log.debug('Anyone can manage own announcements.')
return announcement
- elif member_role == announcer_role:
- if member_role == highest_role(server.roles):
- log.debug('Member {} with role {} on server {} can edit announcements.'.format(member.name, member_role.name, server.name))
+ if announcement['channel_id'] != 'private':
+ server = client.get_server(announcement['server_id'])
+ announcer = server.get_member(announcement['member_id'])
+ announcer_role = highest_role(announcer.roles)
+ member_role = highest_role(member.roles)
+ if member_role > announcer_role:
+ log.debug('Member {} on server {} can edit announcements from lower role.'.format(member.name, server.name))
return announcement
+ elif member_role == announcer_role:
+ if member_role == highest_role(server.roles):
+ log.debug('Member {} with role {} on server {} can edit announcements.'.format(member.name, member_role.name, server.name))
+ return announcement
+
+ if command == 'create':
+ 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'])
+ bot = server.get_member(client.user.id)
+ member_role = highest_role(member.roles)
+ bot_role = highest_role(bot.roles)
+ if member_role > bot_role:
+ log.debug('Member {} with role {} on server {} can create announcements.'.format(member.name, member_role.name, server.name))
+ return announcement
yield from say(channel, 'hiss!')
return False
return
servers = []
if message.channel.is_private:
+ servers.append('private')
for server in client.servers:
if message.author in server.members:
servers.append(server)
return
lines = []
- server = client.get_server(announcement['server_id'])
- member = server.get_member(announcement['member_id'])
+ if announcement['channel_id'] == 'private':
+ for server in client.servers:
+ try:
+ member = server.get_member(announcement['member_id'])
+ except:
+ pass
+ else:
+ server = client.get_server(announcement['server_id'])
+ member = server.get_member(announcement['member_id'])
text = '**announcement {} by {}'.format(id, member.name)
now = int(time.time())
if announcement['last_spoke']:
else:
text += ' tell <@{}>'.format(mention)
if announcement['channel_id'] != announcement['voice_id']:
- text += ' in <#{}>'.format(announcement['channel_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']:
yield from say(message.channel, '\n'.join(lines))
@asyncio.coroutine
+def create_announcement(message, raw):
+ # announcement create [params]: <text>
+ parts = raw.split(':')
+ params = parts[0]
+ if len(parts) > 1:
+ text = ':'.join(parts[1:]).strip()
+ else:
+ text = None
+ log.debug('announce create params {} text {}'.format(params, text))
+
+ create = { 'member_id': message.author.id }
+ if text:
+ create['message'] = text
+
+ args = shlex.split(params)
+ log.info(args)
+ ok = False
+ i = 0
+ while i < len(args):
+ arg = args[i].lower()
+ if i > len(args) - 1:
+ break
+ param = args[i + 1]
+ log.info('{}: {}={}'.format(i, arg, param))
+ ok = False
+
+ if arg == 'create':
+ i -= 1
+ ok = True
+ elif arg == 'once':
+ create['interval'] = 0
+ i -= 1
+ ok = True
+ elif arg in ['from', 'to']:
+ k = 'start_date' if arg == 'from' else 'end_date'
+ if re.match(r'\d\d\d\d-\d\d-\d\d[T\s]\d\d:\d\d:\d\dZ?', param):
+ create[k] = param
+ ok = True
+ else:
+ break
+ elif arg == 'every':
+ m = re.match(r'(\d+)s?', param)
+ if m is not None:
+ create['interval'] = m.group(1)
+ ok = True
+ else:
+ break
+ elif arg == 'tell':
+ if param == 'me':
+ create['mention'] = message.author.id
+ ok = True
+ else:
+ m = re.match(r'<@((?:&?|#)\d+)>', param)
+ if m is None:
+ m = re.match('r@((?:&?|#)\d+)', param)
+ if m is None:
+ m = re.match('r@(everyone|here)', param)
+ if m is not None:
+ create['mention'] = m.group(1)
+ ok = True
+ else:
+ break
+ elif arg in ['in', 'voice']:
+ k = 'channel_id' if arg == 'in' else 'voice_id'
+ if param == 'here':
+ if arg != 'in':
+ break
+ if message.channel.is_private:
+ create[k] = 'private'
+ ok = True
+ else:
+ create[k] = message.channel.id
+ ok = True
+ elif param == 'private':
+ if arg != 'in':
+ break
+ create[k] = 'private'
+ ok = True
+ else:
+ m = re.match(r'<#(\d+)>', param)
+ if m is None:
+ m = re.match(r'#(\d+)', param)
+ if m is not None:
+ create[k] = m.group(1)
+ ok = True
+ else:
+ break
+ elif arg == 'sound':
+ create['sound'] = param
+ ok = True
+ elif arg == 'probability':
+ create['probability'] = param
+ ok = True
+ else:
+ yield from say(message.channel, 'What is {}?'.format(arg))
+ return
+
+ if ok:
+ i += 2
+ else:
+ break
+
+ if not ok:
+ log.info('Failed to parse announcement. Got: {}'.format(create))
+ yield from say(message.channel, '{}?'.format(arg))
+ return
+
+ if 'channel_id' not in create:
+ yield from say(message.channel, 'Missing channel!')
+ return
+
+ if create['channel_id'] == 'private':
+ create['server_id'] = 'private'
+ if 'mention' in create:
+ if create['mention'] != message.author.id:
+ yield from say(message.channel, "Can't mention someone else in private message!")
+ return
+ create['mention'] = None
+ if 'voice_id' in create or 'sound' in create:
+ yield from say(message.channel, 'No sounds for private messages!')
+ return
+ else:
+ channel = client.get_channel(create['channel_id'])
+ if not channel:
+ yield from say(message.channel, 'Invalid channel!')
+ return
+ server = channel.server
+ create['server_id'] = server.id
+
+ announce = yield from can_manage_announcements(message.author, message.channel, 'create', create = create)
+ if not announce:
+ return
+
+ log.info('Creating announcement: {}'.format(create))
+ id = db.create_announcement(client, **create)
+ if id:
+ yield from say(message.channel, id)
+ yield from play_sound(voice_channel_for_channel(message.channel), 'purr.wav', join = False)
+ else:
+ yield from say(message.channel, 'yelp!')
+
+@asyncio.coroutine
def delete_announcement(message, id):
announcement = yield from can_manage_announcements(message.author, message.channel, 'delete', id = id)
if not announcement:
if m is None:
yield from list_announcements(message)
return
- args = shlex.split(m.group(1))
+ text = m.group(1)
+ args = shlex.split(text)
if len(args) == 1:
if args[0] == 'list':
yield from list_announcements(message)
yield from schedule_announcement(message, args[1], pause = True)
elif args[0] == 'resume':
yield from schedule_announcement(message, args[1], pause = False)
+ elif args[0] == 'create':
+ yield from create_announcement(message, text)
@asyncio.coroutine
def announce(announcement):
- channel = client.get_channel(str(announcement['channel_id']))
+ channel = None
+ channel_id = str(announcement['channel_id'])
+ if channel_id == 'private':
+ for server in client.servers:
+ try:
+ member = server.get_member(announcement['member_id'])
+ channel = yield from client.start_private_message(member)
+ break
+ except:
+ pass
+ else:
+ channel = client.get_channel(channel_id)
if channel is None:
log.warning("Can't get channel for announcement {}".format(announcement['message']))
+ # Set last_spoke so we don't spam.
+ db.set_announcement(client, announcement['id'], announcement['digest'] if 'digest' in announcement else None)
return
voice_only = announcement['voice_id'] == announcement['channel_id']
if len(servers):
sql += ' and server_id in ({})'.format(', '.join(['?'] * len(servers)))
for server in servers:
- params.append(server.id)
+ if type(server) == discord.server.Server:
+ params.append(server.id)
+ else:
+ params.append(server)
cursor = self.query(sql, params)
for row in cursor.fetchall():
yield dict(row)
def get_announcements(self, client):
now = self.now()
- cursor = self.query('select id, server_id, channel_id, voice_id, sound, mention, start_date, end_date, interval, probability, last_spoke, 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 (last_spoke is null or last_spoke < ? - interval) and bot_id=?', [now, now, now, client.user.id])
+ cursor = self.query('select id, server_id, member_id, channel_id, voice_id, sound, mention, start_date, end_date, interval, probability, last_spoke, 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 (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)
self.close_db()
self.dbh.commit()
self.close_db()
+ def create_announcement(self, client, **args):
+ id = self.uuid()
+
+ values = []
+ params = [id, client.user.id]
+ for k, v in args.items():
+ values.append(k)
+ params.append(v)
+ sql = 'insert into announcements (id, bot_id, {}) values (?, ?, {})'.format(', '.join(values), ', '.join(['?'] * len(values)))
+ cursor = self.query(sql, params)
+ if cursor.rowcount:
+ self.dbh.commit()
+ ret = id
+ else:
+ log.error('Failed to create announcement {}: {}'.format(id, args))
+ self.dbh.rollback()
+ ret = None
+ self.close_db()
+ return ret
+
def update_announcement(self, client, id, **args):
values = []
params = []