def setup(): #Create cardname table try: cur.execute( 'CREATE TABLE cardnames (id SERIAL PRIMARY KEY, name varchar(320))' ) conn.commit() except Exception as e: cur.execute('ROLLBACK') conn.commit() SendErrorMail(e, traceback.format_exc()) #Create requests table try: cur.execute( 'CREATE TABLE requests (id SERIAL PRIMARY KEY, name varchar(320), requester varchar(50), subreddit varchar(50), requesttimestamp timestamp DEFAULT current_timestamp)' ) conn.commit() except Exception as e: cur.execute('ROLLBACK') conn.commit() SendErrorMail(e, traceback.format_exc()) #Create comments table try: cur.execute( 'CREATE TABLE comments (commentid varchar(16) PRIMARY KEY, commenter varchar(50), subreddit varchar(50), hadRequest boolean, requesttimestamp timestamp DEFAULT current_timestamp)' ) conn.commit() except Exception as e: cur.execute('ROLLBACK') conn.commit() SendErrorMail(e, traceback.format_exc())
def buildResponse(self, comment): try: reply = '' normalRequests = self.getNormalRequests(comment) expandedRequests = self.getExpandedRequests(comment) #If there are 10 or more expanded requests, turn them all into normal requests #Reddit has a 10k character limit if (len(normalRequests) + len(expandedRequests)) >= 8: normalRequests.extend(expandedRequests) expandedRequests = [] for card in normalRequests: requestComment = buildRequestComment(card, False) if requestComment: reply += requestComment + '\n\n---\n\n' for card in expandedRequests: requestComment = buildRequestComment(card, True) if requestComment: reply += requestComment + '\n\n---\n\n' if reply: reply += getSignature() return reply else: return None except Exception as e: SendErrorMail(e, traceback.format_exc())
def updateTCGCardlist(): global TCGArray try: numOfChanges = 0 #gets the cardname list from the server print("Updating card set.") req = requests.get("http://yugiohprices.com/api/card_names") cardnameList = json.loads(json.dumps(req.json())) #for each card, if it's not in the database, add it for card in cardnameList: cur.execute( "select exists(select * from cardnames where name = %s)", (card, )) if not cur.fetchone()[0]: try: cur.execute("insert into cardnames (name) values (%s)", (card, )) conn.commit() numOfChanges += 1 except Exception as e: cur.execute('ROLLBACK') conn.commit() SendErrorMail(e, traceback.format_exc()) if (not TCGArray) or (numOfChanges > 0): print('Building array.') cur.execute("select name from cardnames") cardnames = cur.fetchall() TCGArray = [] for card in cardnames: TCGArray.append(card[0]) print('Card set updated. Number of changes = ' + str(numOfChanges) + '.') except Exception as e: SendErrorMail(e, traceback.format_exc()) print('Card updating failed.')
def addComment(commentid, requester, subreddit, hadRequest): try: subreddit = str(subreddit).lower() cur.execute( 'INSERT INTO comments (commentid, commenter, subreddit, hadRequest) VALUES (%s, %s, %s, %s)', (commentid, requester, subreddit, hadRequest)) conn.commit() except Exception as e: cur.execute('ROLLBACK') conn.commit() SendErrorMail(e, traceback.format_exc())
def addRequest(name, requester, subreddit): try: subreddit = str(subreddit).lower() if ('dewey-defeats-truman' not in subreddit): cur.execute( 'INSERT INTO requests (name, requester, subreddit) VALUES (%s, %s, %s)', (name, requester, subreddit)) conn.commit() except Exception as e: cur.execute('ROLLBACK') conn.commit() SendErrorMail(e, traceback.format_exc())
def commentExists(commentid): try: cur.execute('SELECT * FROM comments WHERE commentid = %s', (commentid, )) if (cur.fetchone()) is None: return False else: return True except Exception as e: cur.execute('ROLLBACK') conn.commit() SendErrorMail(e, traceback.format_exc()) return True
def getClosestTCGCardname(searchText): try: global TCGArray closestCard = difflib.get_close_matches(searchText, TCGArray, 1, 0.95) if closestCard: return closestCard[0] else: return None except Exception as e: SendErrorMail(e, traceback.format_exc()) print("Error finding cards.") return None
def processSubmissions(self, num): subreddits = self.reddit.subreddit(self.subredditList) for submission in subreddits.new(limit=num): #If we've already seen this submission, ignore it if DatabaseHandler.commentExists(submission.id): continue #If the post has been deleted, getting the author will return an error try: author = submission.author.name except Exception as e: continue #If this is one of our own submissions, ignore it if (author == 'YugiohLinkBot'): continue reply = self.requestHandler.buildResponse(submission.selftext) try: if reply: cards = re.findall(r'\[\*\*(.+?)\*\*\]\(', reply) for card in cards: DatabaseHandler.addRequest(card, author, submission.subreddit) if ("VENT THREAD" in submission.title): reply = self.convertCase(True, reply) elif ("happiness thread" in submission.title): reply = self.convertCase(False, reply) DatabaseHandler.addComment(submission.id, author, submission.subreddit, True) submission.add_comment(reply) print("Comment made.\n") else: if ('{' in submission.selftext and '}' in submission.selftext): print('') DatabaseHandler.addComment(submission.id, author, submission.subreddit, False) except Exception as e: SendErrorMail(e, traceback.format_exc()) print("Reddit probably broke when replying:" + str(e) + '\n')
def getCardData(searchText): try: print('Searching Yugipedia for: ' + searchText) wikiURL = getOCGCardURL(searchText) if not wikiURL: wikiURL = getWikiaURL(searchText) if (wikiURL): ocgData = getOCGCardData(wikiURL) formattedData = formatOCGData(ocgData) if formattedData: print("(OCG) Found: " + ocgData['name']) return formattedData else: return None except Exception as e: SendErrorMail(e, traceback.format_exc()) return None
def getOCGCardURL(searchText): try: searchResults = requests.get(OCG_BASE_URL + '?action=query&list=search&srsearch=' + searchText + '&srlimit=50&format=json') except Exception as e: SendErrorMail(e, traceback.format_exc()) return None data = searchResults.json()['query']['search'] titles = [item['title'].lower() for item in data] results = difflib.get_close_matches(searchText.lower(), titles, 1, 0.85) if results: for item in data: if item['title'].lower() == results[0]: return getWikiaURL(item['title']) return None
def formatOCGData(data): try: formatted = {} formatted['name'] = data['name'] formatted['wikia'] = getWikiaURL(data['name']) formatted['pricedata'] = getPricesURL(data['name']) formatted['image'] = data['image'] formatted['text'] = data['description'].replace('\n', ' \n') formatted['cardtype'] = data['type'] if formatted['cardtype'] == 'Monster Card': formatted['attribute'] = data['monster_attribute'].upper() formatted['types'] = data['monster_types'] formatted['level'] = data['monster_level'] formatted['att'] = data['monster_attack'] formatted['defn_type'] = 'DEF' formatted['def'] = data['monster_defense'] formatted['pendulum_scale'] = data['pendulum_scale'] if 'link' in ' '.join( str(i[1]).lower() for i in enumerate(formatted['types'])): formatted['leveltype'] = 'Link Arrows' formatted['defn_type'] = 'LINK' elif 'xyz' in ' '.join( str(i[1]).lower() for i in enumerate(formatted['types'])): formatted['leveltype'] = 'Rank' else: formatted['leveltype'] = 'Level' else: formatted['property'] = data['spell_trap_property'] return formatted except Exception as e: SendErrorMail(e, traceback.format_exc()) return None
def getStats(searchText): try: requestDict = {} cur.execute("SELECT COUNT(*) FROM requests") total = int(cur.fetchone()[0]) + 1 cur.execute("SELECT COUNT(*) FROM requests WHERE name = %s", (searchText, )) requestTotal = int(cur.fetchone()[0]) + 1 #+1 since we add to requests AFTER we call this requestDict['total'] = requestTotal totalAsPercentage = (float(requestTotal) / total) * 100 requestDict['totalAsPercentage'] = totalAsPercentage return requestDict except Exception as e: cur.execute('ROLLBACK') conn.commit() SendErrorMail(e, traceback.format_exc()) return None
def getOCGCardData(url): try: html = requests.get(url) ocg = pq(html.text) cardtable = list(ocg('table[class="innertable"]')('tbody').items('tr')) print("In getOCGCardData") data = { 'image': ocg('div[class="cardtable-main_image-wrapper"]')( 'a img').attr["src"], 'name': ocg('h1[id="firstHeading"]').text(), 'type': cardtable[0]('td p a').attr["title"] } if (data['type'] == 'Monster Card'): data['monster_attribute'] = cardtable[1]('td p a').attr["title"] data['monster_types'] = [ monster_type.text().strip() for monster_type in cardtable[2]('td p').items("a") ] if 'Link' in data['monster_types']: data['monster_level'] = '/'.join([ linkarrow.text() for linkarrow in list(cardtable[3]( 'td div').items("div"))[0].items("a") ][8:]) else: data['monster_level'] = int( list(cardtable[3]('td p').items("a"))[0].text()) if 'Pendulum' in data['monster_types']: data['pendulum_scale'] = int( list(cardtable[4]('td p').items("a"))[1].text()) atk_def = [ value.text() for value in cardtable[5]('td p').items("a") ] else: data['pendulum_scale'] = None atk_def = [ value.text() for value in cardtable[4]('td p').items("a") ] data['monster_attack'] = process_string(atk_def[0]) data['monster_defense'] = process_string(atk_def[1]) elif (data['type'] == 'Spell Card' or data['type'] == 'Trap Card'): data['spell_trap_property'] = list( cardtable[1]('td p').items("a"))[0].text() if (data['type'] == 'Monster Card'): for i, m_type in enumerate(data['monster_types']): data['monster_types'][i] = data['monster_types'][i].strip() description_element = cardtable[-1]('td div').html() description_element = re.sub(r'</dt>', ': </dt>' + BREAK_TOKEN, description_element) description_element = re.sub(r'</dd>', '</dd>' + BREAK_TOKEN, description_element) description_element = re.sub(r'<br ?/?>', BREAK_TOKEN, description_element) description_element = re.sub(r'<a href=[^>]+>', '', description_element) description_element = re.sub(r'</a>', '', description_element) description_element = pq(description_element).text() description_element = description_element.replace(BREAK_TOKEN, '\n') description_element = re.sub(r':(?=\w)', ': ', description_element) description_element = re.sub(r'\.(?=\w)', '. ', description_element) data['description'] = re.sub(r'(\n)+', '\n', description_element) return data except Exception as e: SendErrorMail(e, traceback.format_exc()) return None
def formatCardData(card, isExpanded): try: if isExpanded: requestStats = DatabaseHandler.getStats(card['name']) if card['cardtype'] == 'Monster Card' or card['cardtype'].lower( ) == 'monster': return MONSTER_CARD_TEMPLATE_EXPANDED.format( name='[**{}**]'.format(card['name']), image='({})'.format(card['image']) if card['image'] else '(http://i.imgur.com/paNkvJ5.jpg)', wikia='[Yugipedia]({})'.format(card['wikia']), infosyntax=', ' if card['pricedata'] else '', pricedata='[($)]({})'.format(card['pricedata']) if card['pricedata'] else '', leveltype='**{}**: '.format(card['leveltype']) if card['leveltype'] else '', level='{}, '.format(card['level']) if card['level'] else '', cardtype='**Category**: {}, '.format( card['cardtype'].title()), types='**Type**: {}, '.format(' / '.join( str(i[1]) for i in enumerate(card['types']))), pendulum_scale='**Pendulum Scale**: {}, '.format( card['pendulum_scale']) if card['pendulum_scale'] else '', attribute='**Attribute**: {}'.format( card['attribute'].upper()), text='>{}'.format(card['text']), att='>**ATK**: {}'.format(card['att']), defn_type=', **{}**: '.format(card['defn_type']) if card['defn_type'] is not None else '', defn='{}'.format(card['def']) if card['def'] is not None else '', stats= '**Stats**: {total} requests - {percentage}% of all requests' .format(total=requestStats['total'], percentage=str( round(requestStats['totalAsPercentage'], 2)))) else: return SPELL_CARD_TEMPLATE_EXPANDED.format( name='[**{}**]'.format(card['name']), image='({})'.format(card['image']) if card['image'] else '(http://i.imgur.com/paNkvJ5.jpg)', wikia='[Yugipedia]({})'.format(card['wikia']), infosyntax=', ' if card['pricedata'] else '', pricedata='[($)]({})'.format(card['pricedata']) if card['pricedata'] else '', cardtype='**Category**: {}, '.format( card['cardtype'].title()), cardproperty='**Property**: {}'.format(card['property']), text='>{}'.format(card['text']), stats= '**Stats**: {total} requests - {percentage}% of all requests' .format(total=requestStats['total'], percentage=str( round(requestStats['totalAsPercentage'], 2)))) else: if card['cardtype'].lower() == 'Monster Card': return MONSTER_CARD_TEMPLATE_NORMAL.format( name='[**{}**]'.format(card['name']), image='({})'.format(card['image']) if card['image'] else '(http://i.imgur.com/paNkvJ5.jpg)', wikia='[Yugipedia]({})'.format(card['wikia']), infosyntax=', ' if card['pricedata'] else '', pricedata='[($)]({})'.format(card['pricedata']) if card['pricedata'] else '') else: return SPELL_CARD_TEMPLATE_NORMAL.format( name='[**{}**]'.format(card['name']), image='({})'.format(card['image']) if card['image'] else '(http://i.imgur.com/paNkvJ5.jpg)', wikia='[Yugipedia]({})'.format(card['wikia']), infosyntax=', ' if card['pricedata'] else '', pricedata='[($)]({})'.format(card['pricedata']) if card['pricedata'] else '') except Exception as e: SendErrorMail(e, traceback.format_exc())
from YugiohLinkBot import YugiohLinkBot from ErrorMailer import SendErrorMail import traceback import Config bot = YugiohLinkBot(Config.subredditlist) try: while (1): bot.run() except Exception as e: print("Shutting down bot: " + str(e)) SendErrorMail(e, traceback.format_exc()) try: raise e except KeyboardInterrupt: exit(0)
def run(self): try: print("Starting stream") commentStream = self.reddit.subreddit( self.subredditList).stream.comments() for comment in commentStream: if ((time.time() - self.updateTime) > Config.tcgUpdateInterval * 60 * 60): DatabaseHandler.updateTCGCardlist() self.updateTime = time.time() if ((time.time() - self.submissionsLastProcessed) > Config.submissionProcessingInterval * 60 * 60): self.submissionProcessor.processSubmissions(100) self.submissionsLastProcessed = time.time() #print("Found comment") #If we've already seen this comment, ignore it if DatabaseHandler.commentExists(comment.id): continue #If the post has been deleted, getting the author will return an error try: author = comment.author.name except Exception as e: continue #If this is one of our own comments, ignore it if (author == 'YGOLinkBot'): continue reply = self.requestHandler.buildResponse(comment.body) try: if reply: cards = re.findall(r'\[\*\*(.+?)\*\*\]\(', reply) for card in cards: DatabaseHandler.addRequest(card, author, comment.subreddit) if ("VENT THREAD" in comment.link_title): reply = self.submissionProcessor.convertCase( True, reply) elif ("happiness thread" in comment.link_title): reply = self.submissionProcessor.convertCase( False, reply) comment.reply(reply) DatabaseHandler.addComment(comment.id, author, comment.subreddit, True) print("Comment made.\n") else: if ('{' in comment.body and '}' in comment.body): print('') DatabaseHandler.addComment(comment.id, author, comment.subreddit, False) except Exception as e: SendErrorMail(e, traceback.format_exc()) print("Reddit probably broke when replying:" + str(e) + '\n') except Exception as e: SendErrorMail(e, traceback.format_exc()) print(e) pass