async def command_facereplace(self, user, in_channel, parsed): try: img_url = parsed['image'][1:-1] image_file = await get_image(img_url) except ValueError: return MessageCommand(channel=in_channel, user=user, text='Image {} not found.'.format(img_url)) filename = next(tempfile._get_candidate_names()) + '.png' with open(image_file, 'rb') as image: try: faces = await detect_face(image, self.MAX_FACES) except errors.HttpError: return MessageCommand( channel=in_channel, user=user, text='Failed API call. Image may be too large.') # Reset the file pointer, so we can read the file again image.seek(0) if faces: replace_faces(image, faces, filename) else: return MessageCommand(channel=in_channel, user=user, text='No faces found.') os.remove(image_file) return UploadCommand(channel=in_channel, user=user, file_name=filename, delete=True)
async def stock_info(self, user, in_channel, parsed): stock_name = parsed['stock'].upper() try: if stock_name == 'ALL': stocks = list(StockDoc.objects()) stocks.sort(key=attrgetter('name')) else: stocks = [StockDoc.objects.get(name=stock_name)] except DoesNotExist: return MessageCommand( channel=in_channel, user=user, text='Stock {} does not exist'.format(stock_name)) result = [] for stock in stocks: dividend = self.compute_dividend(stock) buy_price = self.compute_price(dividend, stock) result.append( '`{}{} - {:3d} deaths - Dividend: {:4.01f} - Price: {:3d} - Shares {:3d}/{} ({:4.0%})`' .format(' ' * (4 - len(stock.name)), stock.name, self.get_deaths(stock.target_user), dividend, buy_price, stock.quantity, stock.total, stock.quantity / stock.total)) return MessageCommand(user=user, channel=in_channel, text='\n'.join(result))
async def command_list(self, user, in_channel, parsed): to_user = user if 'user' in parsed and to_user in self.admins: user = parsed['user'] elif 'user' in parsed: return MessageCommand(user=to_user, text="Not allowed.") else: user = to_user one_channel = 'channel' in parsed reacts = defaultdict(list) for react in (ReactDoc.objects(user=user, channel=parsed['channel']) if one_channel else ReactDoc.objects(user=user)): reacts[react.channel].append((react.regex, react.emoji)) for l in reacts.values(): l.sort() res = [] for channel, l in reacts.items(): res.append("Reactions in {}".format(channel)) for i, (reg, em) in enumerate(l): res.append("\t{}. {} --> :{}:".format(i, reg, em)) return MessageCommand(channel=in_channel if 'here' in parsed else None, user=to_user, text='\n'.join(res))
async def _history_handler(self, user, in_channel, image_url, hist_list): if not hist_list: return try: image_file = await get_image(image_url) if image_url else None except ValueError: return MessageCommand(channel=None, user=user, text='Image {} not found.'.format(image_url)) text = (rec.text for rec in hist_list) # Leslie's regex for cleaning mentions, emoji and uploads text = (re.sub('<[^>]*>|:[^\s:]*:|uploaded a file:', '', t) for t in text) try: out_file = await WordcloudBot.make_wordcloud( ' '.join(text), image_file) except NotImplementedError as e: return MessageCommand( channel=None, user=user, text="Apparently can't handle that image: {}".format( e.message())) return UploadCommand(channel=in_channel, user=user, file_name=out_file, delete=True)
async def command_emoji(self, user, in_channel, parsed): emotify = False if 'channel' in parsed and parsed['channel'] not in self.channels: out_text = "Channel {} not recognized or not permitted.".format( parsed['channel']) out_channel = None elif 'channel' in parsed: curr_time = time.time() diff = int(self.next_use[user] - curr_time) if diff > 0: out_text = "You can send another message in {} seconds.".format( diff) out_channel = None else: self.next_use[user] = curr_time + self.delay emotify = True out_channel = parsed['channel'] else: out_channel = in_channel emotify = True if emotify: out_text = self.emotify(parsed) if out_channel is None: return MessageCommand(text=out_text, user=user) else: return MessageCommand(text=out_text, channel=out_channel)
async def frog_monitor(self, user, in_channel, message): match = self.reg.search(message) if match: return MessageCommand(text='what _{}_ {}'.format( match.group(1), match.group(2)), channel=in_channel, user=user)
async def command_bind(self, user, in_channel, parsed): output = parsed['output'] key = parsed['key'] out_text = '' if len(output) > self.max_len: out_text = 'Binds must be less than {} characters long'.format( self.max_len) out_channel = None elif key in self.binds: out_text = '{} is already bound to {}.'.format( key, self.binds[key].output) out_channel = None else: self.binds[key] = Bind(user, output) # Write bind to mongo bind = BindDoc(key=key, user=user, output=output) bind.save() self.current_bind_exprs[key] = (Literal(key)) self.update_bind_expr() if out_text: return MessageCommand(channel=out_channel, user=user, text=out_text)
async def sell_stocks(self, user, in_channel, parsed): err_message = None stock_name = parsed['stock'].upper() try: stock = StockDoc.objects.get(name=stock_name) except DoesNotExist: err_message = 'Stock {} not found'.format(stock_name) if not err_message: amount = max(int(parsed['amount']), 1) if 'amount' in parsed else 1 user_holdings = StockHoldingsDoc.objects.get(user=user) stock_amounts = user_holdings.stocks if stock_amounts[stock_name] < amount: err_message = 'You do not have enough stock to fulfill that sale.' else: dividend = self.compute_dividend(stock) money = self.compute_price(dividend, stock, amount=amount, sell=True) await economy.give(user, money) stock.update(quantity=stock.quantity + amount) stock_amounts[stock_name] -= amount if stock_amounts[stock_name] == 0: del stock_amounts[stock_name] user_holdings.update(stocks=stock_amounts) if err_message: return MessageCommand(channel=in_channel, user=user, text=err_message)
async def command_level(self, user, in_channel, parsed): required_level = self.level_commands[parsed['command'].lower()] user_level = await economy.level(user) secondary = required_level == 1 cost = self.level_costs[required_level] user_currency = await economy.user_currency(user, secondary=secondary) if user_level < required_level: message = 'You need to pull yourself up by your bootstraps.' elif user_level > required_level: message = 'You\'ve already done that' elif user_currency < cost: if secondary: message = 'You need {} grades'.format(cost) else: message = 'You need {} {}. Maybe if you worked harder?'.format(cost, self.currency_name) else: if secondary: await economy.set(user, 0, secondary=True) message = 'You\'ve graduated!' else: await economy.give(user, -cost) message = 'You\'ve enrolled!' await economy.level_up(user) return MessageCommand(channel=in_channel, user=user, text=message)
async def command_judge(self, user, in_channel, parsed): sent = self.predict(parsed['text'][0]) sent *= 100 decimals = parsed['decimals'] if 'decimals' in parsed else '0' format_str = 'Positive: {:.' + decimals + 'f}% Negative: {:.' + decimals + 'f}%' return MessageCommand(text=format_str.format(sent[2], sent[0]), channel=in_channel, user=user)
async def command_check(self, user, in_channel, parsed): secondary = self.check_commands[parsed['command'].lower()] money = await economy.user_currency(user, secondary=secondary) currency_name = 'grades' if secondary else self.currency_name return MessageCommand( text='You have {} {}'.format(int(money), currency_name), channel=in_channel, user=user)
async def command_twitch(self, user, in_channel, parsed): # None if not found twitch_channel = parsed.get('twitch_channel') if twitch_channel and not twitch_channel.startswith('#'): twitch_channel = '#' + twitch_channel if twitch_channel not in self.markov.probabilities: out_text = "Channel {} not recognized. If you would like this channel added, ask a bot admin.".format( twitch_channel) out_channel = None else: out_channel = in_channel out_text = self.markov.generate_message(twitch_channel) if out_channel is None: return MessageCommand(text=out_text, user=user) else: return MessageCommand(text=out_text, channel=out_channel)
async def command_hankey(self, user, in_channel, parsed): if await economy.user_currency(user) >= self.cost: self.target = parsed['target'] await economy.give(user, -self.cost) self.cost += 1 with open(SAVE_FILE, 'wb') as f: pickle.dump(self.cost, f) else: return MessageCommand(text="no", channel=in_channel, user=user)
async def _quoter_callback(self, out_channel, user, hist_list): if not hist_list: return None quote = random.choice(hist_list) year = time.strftime('%Y', time.localtime(float(quote.time))) return MessageCommand(channel=out_channel, user=user, text='> {}\n-{} {}'.format( quote.text, quote.user, year))
async def market_index(self, user, in_channel, parsed): total_dividend = 0 stock_objs = list(StockDoc.objects()) for stock in stock_objs: total_dividend += self.compute_dividend(stock) * stock.total return MessageCommand(channel=in_channel, user=user, text='The {} is at {}'.format( self.index_name, int(total_dividend / len(stock_objs))))
async def slots(self, user, in_channel, parsed, rigged=None): bet = int(parsed['bet']) bank = await economy.user_currency(user) msg = '' if bank >= bet: reels = rigged if rigged else list(random.choice( self.slots_sym, 3)) jacks = reels.count(self.slots_jackpot_symbol) jackpot = False if jacks: if jacks == 3: won = (await casino.get_jackpot('slots')) jackpot = True elif jacks == 2: won = bet * 4 else: won = 0 else: won = bet * 10 if reels.count(reels[0]) == 3 else 0 if won or jackpot: # you can win the jackpot when it's 0... # only one command can execute at a time, so no race if jackpot: await casino.update_jackpot('slots', -won) await casino.record_win(user, 'slots', won) msg = 'JACKPOT!!!! ' msg += '{0} won {1} {2}!'.format(user, int(won), self.currency_name) else: msg = 'Try again.' await casino.update_jackpot('slots', bet * self.slots_contribution) await economy.give(user, won - bet) await casino.record(user, 'slots', won - bet) return MessageCommand(text='{0}\n{1} Jackpot is {2}.'.format( ''.join(reels), msg, int(await casino.get_jackpot('slots'))), channel=in_channel, user=user) else: return MessageCommand(text='Too poor! Sad.', channel=in_channel, user=user)
async def buy_stocks(self, user, in_channel, parsed): err_message = None stock_name = parsed['stock'].upper() try: stock = StockDoc.objects.get(name=stock_name) except DoesNotExist: err_message = 'Stock {} not found'.format(stock_name) if not err_message: amount = max(int(parsed['amount']), 1) if 'amount' in parsed else 1 if stock.target_user == user: err_message = 'You cannot buy stock in yourself.' elif stock.quantity < amount: err_message = 'Not enough stocks in stock.' else: dividend = self.compute_dividend(stock) cost = self.compute_price(dividend, stock, amount=amount) if await economy.user_currency(user) < cost: err_message = 'You are too poor.' if err_message: return MessageCommand(user=user, channel=in_channel, text=err_message) stock.update(quantity=stock.quantity - amount) user_stocks = get_or_create_user(user) stock_amounts = user_stocks.stocks stock_amounts[stock_name] = stock_amounts.get(stock_name, 0) + amount user_stocks.update(stocks=stock_amounts) await economy.give(user, -cost) return MessageCommand( channel=in_channel, user=user, text='{} bought {} share{} of {} for {} {}.'.format( user, amount, 's' if amount > 1 else '', stock_name, cost, self.currency_name))
async def command_print_bind(self, user, in_channel, parsed): raw = self.binds[parsed['key'][0]].output args = parsed['formats'].strip().split( sep=' ') if 'formats' in parsed else [] out_channel = in_channel try: output = raw.format(*args) except IndexError: output = "Not enough format arguments supplied, got {}".format( len(args)) out_channel = None return MessageCommand(channel=out_channel, user=user, text=output)
async def command_clear(self, user, in_channel, parsed): channel = parsed['channel'] index = int(parsed['index']) react_objs = {(r.regex, r.emoji): r for r in ReactDoc.objects(user=user, channel=channel)} reacts = sorted(react_objs) if not 0 <= index < len(reacts): text = 'Invalid reaction number.' else: r_obj = react_objs[reacts[index]] r_obj.delete() text = 'Reaction deleted.' return MessageCommand(user=user, channel=in_channel, text=text)
async def available_stocks(self, user, in_channel, parsed): result = [] stock_dividends = [ (int(self.compute_price(self.compute_dividend(stock), stock)), stock) for stock in StockDoc.objects() ] stock_dividends.sort(reverse=True, key=itemgetter(0)) for price, stock in stock_dividends: result.append('*{}* {}'.format(stock.name, price)) out_message = (' | '.join(result) + '\n' + datetime.fromtimestamp( self.next_dividend_time).strftime('Next dividend %c {}').format( self.timezone)) return MessageCommand(user=user, channel=in_channel, text=out_message)
async def command_poll(self, user, in_channel, parsed): options = parsed['comma_list'] noptions = len(options) if not 2 <= noptions <= 10: text = 'Invalid number of options provided. {} received, please provide between 2 and 10.'.format( noptions) cb = None else: text = "Please vote:\n" text += "\n".join( [a + " " + b for a, b in zip(self.emoji, options)]) cb = partial(self.response_react, self.emoji[:noptions]) return MessageCommand(text=text, channel=in_channel, user=user, success_callback=cb)
async def _extrema_callback(self, field, out_channel, user, hist_list, target_user=None): if not hist_list: return q_time = max(self._complete_cache(hist_list, user=target_user), key=lambda obj: getattr(obj, field)).time quote = next(r for r in hist_list if r.time == q_time) year = time.strftime('%Y', time.localtime(float(quote.time))) return MessageCommand(channel=out_channel, user=user, text='> {}\n-{} {}'.format( quote.text, quote.user, year))
async def check_stocks(self, user, in_channel, parsed): user_obj = get_or_create_user(user) stocks = user_obj.stocks total_dividend = 0 for stock_name, amount in stocks.items(): stock_obj = StockDoc.objects.get(name=stock_name) dividend = self.compute_dividend(stock_obj) total_dividend += amount * dividend lines = [] lines.extend('`{}{}`: {}'.format(stock, ' ' * (4 - len(stock)), amount) for stock, amount in sorted(stocks.items())) lines.append('Your next dividend payment will be {} {}.'.format( int(total_dividend), self.currency_name)) return MessageCommand(user=user, channel=in_channel, text='\n'.join(lines))
async def command_unbind(self, user, in_channel, parsed): key = parsed['key'] out_text = None if key not in self.binds: out_text = '{} is not bound.'.format(key) out_channel = None elif user not in self.admins and self.binds[key].user != user: out_text = 'You may only unbind your own binds.' out_channel = None else: bind_obj = BindDoc.objects(key=key) bind_obj.delete() del self.current_bind_exprs[key] del self.binds[key] self.update_bind_expr() if out_text: return MessageCommand(channel=out_channel, user=user, text=out_text)
async def _stats_callback(self, out_channel, user, hist_list, target_user=None): if not hist_list: return softmaxes = [ (obj.neg_sent, obj.neut_sent, obj.pos_sent) for obj in self._complete_cache(hist_list, user=target_user) ] counts = [0, 0] for s in softmaxes: # Exclude neutral val counts[np.argmax((s[0], s[2]))] += 1 negative, positive = [100 * c / len(softmaxes) for c in counts] return MessageCommand( text='Positive: {:.0f}% Negative: {:.0f}%'.format( positive, negative), user=user, channel=out_channel)
async def command_generate(self, user, in_channel, parsed): target_user = parsed['user'] if 'reddit' in parsed: user_exists = await self._update_reddit_user(target_user) if user_exists: out_channel = in_channel out_message = await self._generate_message(target_user, reddit=True) else: out_channel = None out_message = 'User {} either does not exist or has no self posts or comments'.format( target_user) else: if target_user in self.user_transitions: out_channel = in_channel out_message = await self._generate_message(target_user) else: out_channel = None out_message = 'User {} not found'.format(target_user) return MessageCommand(user=user, channel=out_channel, text=out_message)
async def command_stats(self, user, in_channel, parsed): won, lost, played, jc, jt, jw = await casino.get_stats(user, 'slots') msg = '{game} Stats for {user}:\n\tNet *{net}* {currency} (+{won}/{lost}). Played {played} times.\n\tHit {jack} jackpot(s).' if jt: msg += ' Last jackpot hit was {amount} {currency} at {date:%H:%M:%S %m/%d/%Y} UTC.' else: jt = datetime.utcnow() jw = 0 formatted = msg.format(user=user, game='Slots', net=int(won + lost), currency=self.currency_name, won=int(won), lost=int(lost), played=played, jack=jc, amount=int(jw), date=jt) return MessageCommand(text=formatted, channel=in_channel, user=user)
async def sentiment_monitor(self, user, in_channel, message, timestamp): if not in_channel: return neg, neut, pos = self.predict(message) SentimentDoc(time=timestamp, user=user, channel=in_channel, pos_sent=pos, neut_sent=neut, neg_sent=neg).save() channel_sentiment = self.sentiments[in_channel] channel_sentiment.append(pos + 0.2 > neg) if len(channel_sentiment) > 10: channel_sentiment.pop(0) self.cooldowns[in_channel] -= 1 if self.cooldowns[in_channel] <= 0 and channel_sentiment.count( False) >= 7: self.cooldowns[in_channel] = COOLDOWN return MessageCommand( text='You guys should cheer up. http://placekitten.com/{}/{}'. format(random.randint(300, 500), random.randint(300, 500)), channel=in_channel)
async def command_gain(self, user, in_channel, parsed): level = self.work_commands[parsed['command'].lower()] required_channel = self.channel_order[level] user_level = await economy.level(user) err_message = None if user_level < level: err_message = 'You can\'t do that yet.' elif user_level > level: err_message = 'You\'re better than that now.' elif required_channel != in_channel: err_message = 'None of that in here, go to #{}.'.format(required_channel) else: if level == 0: await economy.give(user, 1) elif level == 1: await economy.give(user, 1, secondary=True) elif level == 2: await economy.give(user, 5) if err_message: return MessageCommand(channel=in_channel, user=user, text=err_message)
async def command_create(self, user, in_channel, parsed): target_channel = parsed['channel'] reg_text = parsed['target_pattern'] emoji = parsed['emoji'][1:-1] if target_channel not in self.out_channels: text = "Reactions in channel {} not permitted.".format( target_channel) elif len(self.reacts[target_channel][user]) >= self.max_per_user: text = "Maximum number of emojis per channel reached." else: try: reg = re.compile(reg_text, re.IGNORECASE) react_obj = ReactDoc(user=user, channel=target_channel, regex=reg_text, emoji=emoji) react_obj.save() self.reacts[target_channel][user].add((reg, emoji)) text = 'Reaction saved' except re.error: text = 'Invalid pattern.' return MessageCommand(user=user, channel=in_channel, text=text)