def bot_dice(channel_id, message): ''' Bot rolls a dice (6 if no number of sides specified), returning a random side ''' sides = 6 # Not default try: if len(message) > 5: sides = int(message[6:]) choice = 1 + random.choice(range(sides)) bot_msg = f'Rolling a {sides}-sided dice.. and got {choice}' bot_send_message(channel_with_id(channel_id), bot_msg, temporary=False) except Exception: bot_msg = 'Please provide a valid number for /dice X, or just /dice for a six-sided dice!' bot_send_message(channel_with_id(channel_id), bot_msg, temporary=False)
def message_prune(token, channel_id, num_messages): ''' Prunes the last num_messages messages from a channel NOTE: this function cannot be placed in message.py due to circular imports ''' # Retrieve data auth_user = user_with_token(token) channel = channel_with_id(channel_id) # Error check if auth_user is None: raise AccessError('Invalid token') elif channel is None: raise InputError('Invalid channel') elif auth_user not in channel.get_all_members(): raise AccessError('Invalid permission') elif auth_user not in channel.get_owner_members( ) and auth_user.get_permission_id() != 1: raise AccessError('Invalid permission for pruning messages') total_messages = len(channel.get_messages()) if num_messages > total_messages: raise InputError( f'Attempted to prune more messages than there are messages in the channel' ) # Prune last num_messages messages del channel.get_messages()[-num_messages:] return {}
def standup_start(token, channel_id, length): ''' For a given channel, start the standup period whereby for the next "length" seconds if someone calls "standup_send" with a message, it is buffered in the standup queue then at the end of the standup period a message will be added to the message queue in the channel from the user who started the standup. Input: token (str), channel_id (int), length (int) Output: time_finish (UNIX timestamp int) ''' # Retrieve data auth_user = user_with_token(token) channel = channel_with_id(channel_id) # Error check if auth_user is None: raise AccessError('Invalid token') elif channel is None: raise InputError('Invalid channel') elif auth_user not in channel.get_all_members(): raise AccessError('User not member of channel') elif length <= 0: raise InputError('Invalid standup time') elif channel.get_standup_status()['is_active']: raise InputError('An active standup is currently running') end_time = channel.start_standup(initiator=auth_user, length=length) return { 'time_finish': end_time, }
def message_send(token, channel_id, message): ''' Sends the message (str) by storing it in the channel with channel_id (int) and sender as user with token (str) Output: message_id (int) of message stored ''' # Retrieve data auth_user = user_with_token(token) channel = channel_with_id(channel_id) # Error check if auth_user is None: raise AccessError('Invalid token') elif channel is None: raise InputError('Invalid channel') elif auth_user not in channel.get_all_members(): raise AccessError('User not in channel') elif not message: raise InputError('Empty message not allowed') elif len(message) > 1000: raise InputError('Message should be 1000 characters or less') # Store message new_message = Message(auth_user, message, current_time()) channel.get_messages().append(new_message) bot_message_parser(token, channel_id, message) return { 'message_id': new_message.get_message_id(), }
def channel_kick(token, channel_id, u_id): ''' Remove user with user id u_id as a member of the channel Input: token (str), channel_id (int), u_id (int) Output: empty dict ''' # Retrieve data auth_user = user_with_token(token) channel = channel_with_id(channel_id) old_user = user_with_id(u_id) # Error check if auth_user is None: raise AccessError('Invalid token') elif channel is None: raise InputError('Invalid channel_id') elif old_user is None: raise AccessError('Invalid u_id') elif auth_user not in channel.get_all_members(): # Flockr owner may not be a channel member raise AccessError('Authorised user is not a member in the channel') elif auth_user not in channel.get_owner_members() and auth_user.get_permission_id() != 1: raise AccessError('Authorised user is not an owner of channel and not Flockr owner') elif old_user in channel.get_owner_members(): # User cannot be an owner of the channel raise InputError('User to be kicked cannot be an owner in the channel') elif old_user not in channel.get_all_members(): raise InputError('User to be kicked is not a member in the channel') # Remove owner channel.get_all_members().remove(old_user) return { }
def channel_invite(token, channel_id, u_id): ''' Invites a user (with user id u_id) to join a channel with ID channel_id. Once invited the user is added to the channel immediately Input: token (str), channel_id (int), u_id (int) Output: empty dict ''' # Retrieve data auth_user = user_with_token(token) channel = channel_with_id(channel_id) invited_user = user_with_id(u_id) # Error check if auth_user is None: raise AccessError('Invalid token') elif channel is None: raise InputError('Invalid channel_id') elif invited_user is None: raise InputError('Invalid u_id') elif auth_user not in channel.get_all_members(): raise AccessError('Authorised user not a member of channel') # Append invited user to all_members (if they're not already a member) if invited_user not in channel.get_all_members(): channel.get_all_members().append(invited_user) return { }
def standup_send(token, channel_id, message): ''' Send a message to get buffered in the standup queue, assuming a standup is currently active Input: token (str), channel_id (int), message (str) Output: empty dict ''' # Retrieve data auth_user = user_with_token(token) channel = channel_with_id(channel_id) # Error check if auth_user is None: raise AccessError('Invalid token') elif channel is None: raise InputError('Invalid channel') elif auth_user not in channel.get_all_members(): raise AccessError('User not member of channel') elif not channel.get_standup_status()['is_active']: raise InputError('An active standup is not currently running') elif not message: raise InputError('Empty message not allowed') elif len(message) > 1000: raise InputError('Message should be 1000 characters or less') # Add message to queue msg = Message(auth_user, message, time_created=current_time()) channel.get_standup_status()['queued_messages'].append(msg) return {}
def bot_help(channel_id): ''' Displays help message ''' bot_msg = ''' Available commands: === General === - /help displays this message - /time displays the current time - /standup X starts a standup for X seconds - /prune X removes the last X messages - requires admin permissions - /kick user_handle kicks a specified user from this channel - requires admin permissions === Hangman === - /hangman start starts a hangman game - /guess X guess a character or word for an active hangman game === Fun Utilities === - /choose A B C. bot randomly chooses from the space-separated arguments - /flip flips a coin - heads or tails? - /dice (X) rolls a X-sided dice, or a six-sided dice by default ''' bot_send_message(channel_with_id(channel_id), bot_msg, temporary=False)
def standup_active(token, channel_id): ''' For a given channel, return whether a standup is active in it, and what time the standup finishes. If no standup is active, then time_finish returns None. Input: token (str), channel_id (int) Output: is_active (bool), time_finish (UNIX timestamp int) ''' # Retrieve data auth_user = user_with_token(token) channel = channel_with_id(channel_id) # Error check if auth_user is None: raise AccessError('Invalid token') elif channel is None: raise InputError('Invalid channel') elif auth_user not in channel.get_all_members(): raise AccessError('User not member of channel') standup_status = channel.get_standup_status() return { 'is_active': standup_status['is_active'], 'time_finish': standup_status['time_finish'], }
def channel_messages(token, channel_id, start): ''' Given a Channel with ID channel_id that the authorised user is part of, return up to 50 messages between index "start" and "start + 50". Message with index 0 is the most recent message in the channel. This function returns a new index "end" which is the value of "start + 50", or, if this function has returned the least recent messages in the channel, returns -1 in "end" to indicate there are no more messages to load after this return. Input: token (str), channel_id (int), start (int) Ouput: dict ''' # Retrieve data auth_user = user_with_token(token) channel = channel_with_id(channel_id) # Error check if auth_user is None: raise AccessError('Invalid token') elif channel is None: raise InputError('Invalid channel_id') elif auth_user not in channel.get_all_members(): raise AccessError('Authorised user not a member of channel') elif start < 0 or start > len(channel.get_messages()): raise InputError('Invalid start index') # Messages originally ordered chronologically - # reverse and retrieve a maximum of 50 most recent messages messages = list(reversed(channel.get_messages()))[start : start + 50] if len(messages) == 0: # The end is reached there are no messages end = -1 else: # The end is also reached if the first message (id) is included in messages first_message_id = channel.get_messages()[0].get_message_id() first_message_reached = any(message.get_message_id() == first_message_id for message in messages) end = -1 if first_message_reached else start + 50 return { 'messages': [ { 'message_id': message.get_message_id(), 'u_id': message.get_sender().get_u_id(), 'time_created': message.get_time_created(), 'message': message.get_message(), 'reacts': [ { 'react_id': react.get_react_id(), 'u_ids': [reactor.get_u_id() for reactor in react.get_reactors()], 'is_this_user_reacted': auth_user in react.get_reactors(), } for react in message.get_reacts() ], 'is_pinned': message.get_is_pinned(), } for message in messages ], 'start': start, 'end': end, }
def message_sendlater(token, channel_id, message, time_sent): ''' Send a message from authorised_user to the channel specified by channel_id automatically at a specified time in the future. Input: token (str), channel_id (int), message (str), time_sent (UNIX timestamp - float) Output: message_id (int) of message to be sent ''' # Retrieve data auth_user = user_with_token(token) channel = channel_with_id(channel_id) time_diff = time_sent - current_time() # Error check if auth_user is None: raise AccessError('Invalid token') elif channel is None: raise InputError('Invalid channel') elif auth_user not in channel.get_all_members(): raise AccessError('User not in channel') elif not message: raise InputError('Empty message not allowed') elif len(message) > 1000: raise InputError('Message should be 1000 characters or less') elif time_diff < 0: raise InputError('Time is in the past') # Note that message will still be sent later even if the user # leaves channel or logs out before message is actually sent new_message = Message(auth_user, message, time_created=time_sent) t = Timer(time_diff, channel.get_messages().append, args=[new_message]) t.start() return { 'message_id': new_message.get_message_id(), }
def bot_choose(channel_id, message): ''' Bot randomly chooses one of the options listed in the /choose command ''' options = message.split(' ')[1:] bot_msg = f'Hmm.. tough choice.. but I choose {random.choice(options)}' bot_send_message(channel_with_id(channel_id), bot_msg, temporary=False)
def channel_addowner(token, channel_id, u_id): ''' Make user with user id u_id an owner of this channel Input: token (str), channel_id (int), u_id (int) Output: empty dict ''' # Retrieve data auth_user = user_with_token(token) channel = channel_with_id(channel_id) new_owner = user_with_id(u_id) # Error check if auth_user is None: raise AccessError('Invalid token') elif channel is None: raise InputError('Invalid channel_id') elif new_owner is None or new_owner not in channel.get_all_members(): raise AccessError('Invalid u_id or user is not a member in the channel') elif auth_user not in channel.get_owner_members() and auth_user.get_permission_id() != 1: raise AccessError('Authorised user is not an owner of channel and not Flockr owner') elif auth_user not in channel.get_all_members(): # Note that Flockr owner may not be a channel member raise AccessError('Authorised user is not a member in the channel') elif new_owner in channel.get_owner_members(): raise InputError('User to be added as an owner is already an owner in the channel') # Add user as owner channel.get_owner_members().append(new_owner) return { }
def channel_leave(token, channel_id): ''' Given a channel ID, the user removed as a member of this channel Input: token (str), channel_id (int) Output: empty dict ''' # Retrieve data auth_user = user_with_token(token) channel = channel_with_id(channel_id) # Error check if auth_user is None: raise AccessError('Invalid token') elif channel is None: raise InputError('Invalid channel_id') elif auth_user not in channel.get_all_members(): raise AccessError('Authorised user not a member of channel') # Remove user from all_members channel.get_all_members().remove(auth_user) # Attempt to remove user from owner_members if they are an owner if auth_user in channel.get_owner_members(): channel.get_owner_members().remove(auth_user) return { }
def bot_time(channel_id): ''' Displays the current time ''' bot_init() channel = channel_with_id(channel_id) bot_msg = f'The current time is {datetime.now().strftime(r"%A %-d %B %Y, %-I:%M %p")}.' bot_send_message(channel, bot_msg, temporary=False)
def bot_hangman_guess(token, channel_id, message): ''' Registers a guess for an active hangman game ''' channel = channel_with_id(channel_id) try: global hangman_status if not hangman_status['active']: raise InputError( 'No active hangman games.. please type /hangman start to start one!' ) # Extract character or word game_win = False if len(message) < 8: raise InputError( "Invalid guess format. Please ensure it's /guess C") elif len(message) == 8: # Character guessed character = message[7].upper() hangman_status['guessed_letters'].add(character) hangman_status['guesses_remaining'] -= 1 # Win condition (all letters in word have been guessed, as a subset) if hangman_status['word_set'].issubset( hangman_status['guessed_letters']): game_win = True else: # Word guessed hangman_status['guesses_remaining'] -= 1 # Check win condition if hangman_status['word'] == message[7:].upper(): game_win = True # Process end condition if game_win: # Win condition (word guessed) user = user_with_token(token) bot_msg = f"Congratulations {user.get_handle()} on guessing the word {hangman_status['word']}!" bot_send_message(channel, bot_msg, temporary=False) bot_hangman_reset() elif hangman_status['guesses_remaining'] == 0: # Loss condition bot_msg = f"Unlucky, the word was {hangman_status['word']}!" bot_send_message(channel, bot_msg, temporary=False) bot_hangman_reset() else: # Continue condition bot_hangman_word_display(channel) except Exception as e: bot_msg = f'Failed to register guess: {e}' bot_send_message(channel, bot_msg, temporary=False)
def bot_message_prune(token, channel_id, message): ''' Wrapper command for pruning messages ''' bot_init() channel = channel_with_id(channel_id) try: auth_user = user_with_token(token) num_messages = int(message[6:]) message_prune(token, channel_id, num_messages) bot_msg = f'{num_messages} messages have been successfully pruned by {auth_user.get_handle()}' bot_send_message(channel, bot_msg, temporary=True) except Exception as e: bot_msg = f'Failed to prune: {e}' bot_send_message(channel, bot_msg, temporary=False)
def bot_kick(token, channel_id, message): ''' Wrapper command for kicking a user from a channel ''' user = user_with_token(token) channel = channel_with_id(channel_id) try: handle = message[6:] kick_user = user_with_handle(handle) if kick_user is None: raise InputError('Please provide a valid user handle!') channel_kick(token, channel_id, kick_user.get_u_id()) bot_msg = f'⚽️ {kick_user.get_handle()} has been kicked by {user.get_handle()}!' bot_send_message(channel, bot_msg, temporary=False) except Exception as e: bot_msg = f'Failed to kick: {e}' bot_send_message(channel, bot_msg, temporary=False)
def bot_hangman_start(channel_id): ''' Starts a new hangman game ''' channel = channel_with_id(channel_id) global hangman_status if hangman_status['active']: # Already active bot_msg = 'Hangman game already in progress!' bot_send_message(channel, bot_msg, temporary=False) bot_hangman_word_display(channel) return word = random.choice(list(english_words_set)).upper() hangman_status = { 'active': True, 'word': word, 'word_set': set(word), 'guessed_letters': set(), 'guesses_remaining': TOTAL_HANGMAN_GUESSES, } bot_hangman_word_display(channel)
def channel_details(token, channel_id): ''' Given a Channel with ID channel_id that the authorised user is part of, provide basic details about the channel Input: token (str), channel_id (int) Output: dict ''' # Retrieve data auth_user = user_with_token(token) channel = channel_with_id(channel_id) # Error check if auth_user is None: raise AccessError('Invalid token') elif channel is None: raise InputError('Invalid channel_id') elif auth_user not in channel.get_all_members(): raise AccessError('Authorised user not a member of channel') return { 'name': channel.get_name(), 'owner_members': [ { 'u_id': owner.get_u_id(), 'name_first': owner.get_name_first(), 'name_last': owner.get_name_last(), 'profile_img_url': owner.get_profile_img_url(), } for owner in channel.get_owner_members() ], 'all_members': [ { 'u_id': member.get_u_id(), 'name_first': member.get_name_first(), 'name_last': member.get_name_last(), 'profile_img_url': member.get_profile_img_url(), } for member in channel.get_all_members() ], }
def channel_join(token, channel_id): ''' Given a channel_id of a channel that the authorised user can join, adds them to that channel Input: token (str), channel_id (int) Output: empty dict ''' # Retrieve data channel = channel_with_id(channel_id) auth_user = user_with_token(token) # Error check if auth_user is None: raise AccessError('Invalid token') elif channel is None: raise InputError('Invalid channel_id') elif not channel.get_is_public() and auth_user.get_permission_id() != 1: raise AccessError('Private channel and authorised user is not Flockr owner') # Adds user to channel if not already in channel (don't want to add duplicate users to list) if auth_user not in channel.get_all_members(): channel.get_all_members().append(auth_user) return { }
def bot_flip(channel_id): ''' Bot flips a coin and returns heads or tails ''' bot_msg = f"It appears to be {random.choice(['heads', 'tails'])}!" bot_send_message(channel_with_id(channel_id), bot_msg, temporary=False)