# This is an quote and post library for a specific thread in the WarOnline forum. import WarClient import requests import re from bs4 import BeautifulSoup import urllib.request as urllib import warnings import schedule import time from tqdm import tqdm warnings.filterwarnings("ignore") # Define the login URL and the thread URL login_url = 'https://waronline.org/fora/index.php?login/login' thread_url = 'https://waronline.org/fora/index.php?threads/warbot-playground.17636/' post_url = "https://waronline.org/fora/index.php?threads/warbot-playground.17636/add-reply" # Sending the message (change that!) message = "Test" # Define the login credentials username = 'WarBot' password = 'naP2tion' # Start a session to persist the login cookie across requests session = requests.Session() def compare_pages(url1, url2): #Compares 2 pages and returns True if they are the same return urllib.urlopen(url1).geturl() == urllib.urlopen(url2).geturl() def remove_non_english_russian_chars(s): # Regular expression to match all characters that are not in English or Russian pattern = '[^0-9A-Za-zА-Яа-яЁё(),.:!;"\s-]' # Replace all matched characters with an empty string return re.sub(pattern, '', s) def login(username=username, password=password, thread_url=thread_url): # Log-In to the forum and redirect to thread # Retrieve the login page HTML to get the CSRF token login_page_response = session.get(login_url) soup = BeautifulSoup(login_page_response.text, 'html.parser') csrf_token = soup.find('input', {'name': '_xfToken'})['value'] # Login to the website login_data = { 'login': username, 'password': password, 'remember': '1', '_xfRedirect': thread_url, '_xfToken': csrf_token } response = session.post(login_url, data=login_data) # Check if the login was successful if 'Invalid login' in response.text: print('Login failed!') exit() def post(message=message, thread_url=thread_url, post_url=post_url, quoted_by="",quote_text="",quote_source=""): #Post a message to the forum (with or without the quote #quote_source is in format 'post-3920992' quote_source = quote_source.split('-')[-1] # Take the numbers only if quoted_by: message = f'[QUOTE="{quoted_by}, post: {quote_source}"]{quote_text}[/QUOTE]{message}' #message = f'[QUOTE="{quoted_by}, data-source=post: {quote_source}"]{quote_text}[/QUOTE]{message}' # optionally add @{quoted_by} to indent the quoter # Retrieve the thread page HTML response = session.get(thread_url) # Parse the HTML with BeautifulSoup soup = BeautifulSoup(response.text, 'html.parser') # Extract the _xfToken value from the hidden form field xf_token = soup.find('input', {'name': '_xfToken'}).get('value') print(xf_token) # Construct the message data for the POST request message_data = { '_xfToken': xf_token, 'message': message, 'attachment_hash': '', 'last_date': '', '_xfRequestUri': post_url, '_xfWithData': '1', '_xfResponseType': 'json' } response = session.post(post_url, data=message_data) # Check if the post was successful if not response.ok: print('Post failed!') exit() print('Post submitted successfully.') def getMessages(thread_url=thread_url, quotedUser="", startingPage=1): # Returns all the quotes for #username in the specific multi-page thread url allquotes =[] page = startingPage # Counter lastPage = False # Initial values for messangerName and the message ID messengerName = "" messageID = "" quotedID = "" # Patterns to search in the last quote. namePattern = re.compile('data-lb-caption-desc="(.*?) ·') messageIDPattern = re.compile('data-lb-id="(.*?)"') quotedIDPattern = re.compile('data-source="(.*?)"') quotedNamePattern = re.compile('data-quote="(.*?)"') while not lastPage: response = requests.get(thread_url + 'page-' + str(page)) if response.status_code == 200: # Core of the function html_content = response.content # Parse the HTML content using BeautifulSoup soup = BeautifulSoup(html_content, 'html.parser') # Find all the message in the thread page messageData = soup.find_all('div', {'class': 'message-userContent lbContainer js-lbContainer'}) for data in messageData: try: # Get the messager username matchName = namePattern.search(str(data)) if matchName: messengerName = matchName.group(1) # Get the quoted ID matchID = quotedIDPattern.search(str(data)) if matchID: quotedID = matchID.group(1) # Get the message ID matchID = messageIDPattern.search(str(data)) if matchID: messageID = matchID.group(1) matchQuotedName = quotedNamePattern.search(str(data)) if matchQuotedName: quotedName = matchQuotedName.group(1) if quotedUser and (quotedUser != quotedName): continue # Make sure that the messages have a quote inside blockquote = data.find('blockquote') if blockquote: # Extract the text text = data.find('div', {'class': 'bbWrapper'}) for bq in text.find_all('blockquote'): bq.extract() reply = text.get_text().replace('\n', ' ').strip() allquotes.append({'reply': reply, 'messengerName': messengerName, 'messageID': messageID, 'quotedID': quotedID}) except: continue # There was no text in quote, move next #check if that is not a last page if not compare_pages(thread_url + 'page-' + str(page), thread_url + 'page-' + str(page + 1)): page += 1 else: lastPage = True else: lastPage = True return allquotes # Core Engine of the Client def WarOnlineBot(): login(username=username, password=password, thread_url=thread_url) #print("logged in") # All messages (with quotes) by ALL users: allMessages = getMessages(thread_url=thread_url, quotedUser='', startingPage=1) # IDs of the quoted messages, for messages posted by bot: messages_by_bot_IDs = [] for msg in allMessages: # Set a list of replied IDs if msg['messengerName'] == username: messages_by_bot_IDs.append(msg['quotedID'].split(': ')[-1]) # remove empty elements messages_by_bot_IDs = [elem for elem in messages_by_bot_IDs if elem] # All messages (with quotes) sent _FOR_ the Bot: messagesForBot = getMessages(thread_url=thread_url, quotedUser=username, startingPage=1) # IDs of the messages, quoting the bot: messages_for_bot_IDs = [] for msg in messagesForBot: # Set a list of posted message IDs messages_for_bot_IDs.append(msg['messageID'].split('-')[-1]) # remove empty elements messages_for_bot_IDs = [elem for elem in messages_for_bot_IDs if elem] # Filter the unanswered messages for msgID in messages_for_bot_IDs: if msgID in messages_by_bot_IDs: messages_for_bot_IDs.remove(msgID) # print unanswered messages for msg in messagesForBot: if msg['messageID'].split('-')[-1] in messages_for_bot_IDs: quote = remove_non_english_russian_chars(msg['reply']) message = "" while not message: message = WarClient.getReply(message=quote) print('Quote: ', quote) print('Reply: ', message) login(username=username, password=password, thread_url=thread_url) time.sleep(1) #post(message=message, thread_url=thread_url, post_url=post_url, quoted_by=msg['messengerName'], quote_text=quote, quote_source=msg['messageID']) time.sleep(200) # Standby time for server load release if __name__ == '__main__': timeout = 2 # min WarOnlineBot() #login(username=username, password=password, thread_url=thread_url) #post(message=message, thread_url=thread_url, post_url=post_url, quoted_by='WarBot', quote_text='quoted message', quote_source='12345') """ # Start the scheduler while True: login(username=username, password=password, thread_url=thread_url) print("logged in") WarOnlineBot() p_bar = tqdm(range(60 * timeout)) for i in p_bar: p_bar.update(1) p_bar.refresh() time.sleep(1) """