kertser commited on
Commit
71acbff
1 Parent(s): 243d5f5

Upload 4 files

Browse files

Updated the client service with History of Dialogues (Database usage)

Files changed (4) hide show
  1. WarClient.py +6 -4
  2. WarOnline_Chat.py +47 -27
  3. config.py +38 -0
  4. conversationDB.py +51 -16
WarClient.py CHANGED
@@ -1,7 +1,6 @@
1
  import socket
 
2
 
3
- HOST = '129.159.146.88'
4
- PORT = 5000
5
 
6
  def getReply(message):
7
  # Returns a reply from the server
@@ -15,7 +14,7 @@ def getReply(message):
15
 
16
  with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
17
  try:
18
- client_socket.connect((HOST, PORT))
19
  client_socket.sendall(message.encode())
20
  print('Wait...')
21
  data = client_socket.recv(1024)
@@ -28,4 +27,7 @@ def getReply(message):
28
  client_socket.shutdown(socket.SHUT_RDWR)
29
  client_socket.close()
30
  except:
31
- return ""
 
 
 
 
1
  import socket
2
+ import config # Here the constants are stored
3
 
 
 
4
 
5
  def getReply(message):
6
  # Returns a reply from the server
 
14
 
15
  with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
16
  try:
17
+ client_socket.connect((config.HOST, config.PORT))
18
  client_socket.sendall(message.encode())
19
  print('Wait...')
20
  data = client_socket.recv(1024)
 
27
  client_socket.shutdown(socket.SHUT_RDWR)
28
  client_socket.close()
29
  except:
30
+ return ""
31
+
32
+ if __name__ == '__main__':
33
+ pass
WarOnline_Chat.py CHANGED
@@ -1,24 +1,16 @@
1
  # This is an quote and post library for a specific thread in the WarOnline forum.
2
 
3
  import WarClient
 
4
  import requests
5
  import re
6
  from bs4 import BeautifulSoup
7
  import urllib.request as urllib
8
  import warnings
9
- #import schedule
10
  import time
11
- #from tqdm import tqdm
12
- warnings.filterwarnings("ignore")
13
-
14
- # Define the login URL and the thread URL
15
- login_url = 'https://waronline.org/fora/index.php?login/login'
16
- thread_url = 'https://waronline.org/fora/index.php?threads/warbot-playground.17636/'
17
- post_url = "https://waronline.org/fora/index.php?threads/warbot-playground.17636/add-reply"
18
 
19
- # Define the login credentials
20
- username = 'WarBot'
21
- password = 'naP2tion'
22
 
23
  # Start a session to persist the login cookie across requests
24
  session = requests.Session()
@@ -30,6 +22,9 @@ def fixString(S):
30
  S = S.replace(".?", "?")
31
  S = S.replace(",,", ",")
32
  S = S.replace("?.", "?")
 
 
 
33
  S = S.replace(",!", "!")
34
  S = S.replace(",.", ",")
35
  S = S.replace(".]", ".")
@@ -61,7 +56,7 @@ def remove_extra_spaces(s):
61
  s = re.sub(r"\s+([.,])", r"\1", s) # remove spaces before period or comma
62
  return(s)
63
 
64
- def getLastPage(thread_url=thread_url):
65
  # Returns the number of the last page
66
  page = 1 # Starting page
67
  lastPage = False
@@ -74,11 +69,11 @@ def getLastPage(thread_url=thread_url):
74
  return page
75
 
76
 
77
- def login(username=username, password=password, thread_url=thread_url):
78
  # Log-In to the forum and redirect to thread
79
 
80
  # Retrieve the login page HTML to get the CSRF token
81
- login_page_response = session.get(login_url)
82
  soup = BeautifulSoup(login_page_response.text, 'html.parser')
83
  csrf_token = soup.find('input', {'name': '_xfToken'})['value']
84
 
@@ -90,14 +85,14 @@ def login(username=username, password=password, thread_url=thread_url):
90
  '_xfRedirect': thread_url,
91
  '_xfToken': csrf_token
92
  }
93
- response = session.post(login_url, data=login_data)
94
 
95
  # Check if the login was successful
96
  if 'Invalid login' in response.text:
97
  print('Login failed!')
98
  exit()
99
 
100
- def post(message="", thread_url=thread_url, post_url=post_url, quoted_by="",quote_text="",quote_source=""):
101
  #Post a message to the forum (with or without the quote
102
  #quote_source is in format 'post-3920992'
103
  quote_source = quote_source.split('-')[-1] # Take the numbers only
@@ -134,7 +129,7 @@ def post(message="", thread_url=thread_url, post_url=post_url, quoted_by="",quot
134
 
135
  print('Post submitted successfully.')
136
 
137
- def getMessages(thread_url=thread_url, quotedUser="", startingPage=1):
138
  # Returns all the quotes for #username in the specific multi-page thread url
139
  allquotes =[]
140
 
@@ -182,6 +177,7 @@ def getMessages(thread_url=thread_url, quotedUser="", startingPage=1):
182
  if matchID:
183
  messageID = matchID.group(1)
184
 
 
185
  matchQuotedName = quotedNamePattern.search(str(data))
186
  if matchQuotedName:
187
  quotedName = matchQuotedName.group(1)
@@ -216,28 +212,28 @@ def getMessages(thread_url=thread_url, quotedUser="", startingPage=1):
216
  def WarOnlineBot():
217
 
218
  lookUpPages = 5 # How many pages back to look in the thread
219
- startingPage = getLastPage(thread_url=thread_url) - lookUpPages
220
  if startingPage < 1:
221
  startingPage = 1 # Starting page cannot be less than 1
222
 
223
- login(username=username, password=password, thread_url=thread_url)
224
  #print("logged in")
225
 
226
  # All messages (with quotes) by ALL users:
227
- allMessages = getMessages(thread_url=thread_url, quotedUser='', startingPage=startingPage)
228
 
229
  # IDs of the quoted messages, replied by the bot:
230
  messages_by_bot_IDs = []
231
 
232
  for msg in allMessages:
233
  # Set a list of replied messages IDs
234
- if msg['messengerName'] == username: #message posted by the WarBot
235
  messages_by_bot_IDs.append(msg['quotedID'].split(': ')[-1])
236
  # remove empty and repeated elements
237
  messages_by_bot_IDs = list(set([elem for elem in messages_by_bot_IDs if elem]))
238
 
239
  # All messages (with quotes) sent _FOR_ the Bot:
240
- messagesForBot = getMessages(thread_url=thread_url, quotedUser=username, startingPage=startingPage)
241
 
242
  # IDs of the messages, quoting the bot:
243
  messages_for_bot_IDs = []
@@ -262,10 +258,27 @@ def WarOnlineBot():
262
  quote = remove_non_english_russian_chars(msg['reply'])
263
  quote = remove_extra_spaces(quote)
264
 
265
- message = ""
 
266
 
267
  print('Quote: ', originalQuote)
268
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
  FailureCounter = 0 # In case there is a bug in the model
270
  while (not message) and (FailureCounter<3):
271
  message = WarClient.getReply(message=quote)
@@ -281,20 +294,27 @@ def WarOnlineBot():
281
  message = fixString(message)
282
  print('Reply: ', message)
283
 
284
- login(username=username, password=password, thread_url=thread_url)
 
 
 
 
 
 
 
285
  time.sleep(1)
286
- post(message=message, thread_url=thread_url, post_url=post_url, quoted_by=msg['messengerName'], quote_text=originalQuote, quote_source=msg['messageID'])
287
 
288
  time.sleep(10) # Standby time for server load release
289
 
290
 
291
  if __name__ == '__main__':
292
- timeout = 5 # min
293
 
294
  # Start the scheduler
295
  while True:
 
296
  WarOnlineBot()
297
 
298
- timer = range(60 * timeout)
299
  for t in timer:
300
  time.sleep(1)
 
1
  # This is an quote and post library for a specific thread in the WarOnline forum.
2
 
3
  import WarClient
4
+ import conversationDB
5
  import requests
6
  import re
7
  from bs4 import BeautifulSoup
8
  import urllib.request as urllib
9
  import warnings
 
10
  import time
11
+ import config # Here the constants are stored
 
 
 
 
 
 
12
 
13
+ warnings.filterwarnings("ignore")
 
 
14
 
15
  # Start a session to persist the login cookie across requests
16
  session = requests.Session()
 
22
  S = S.replace(".?", "?")
23
  S = S.replace(",,", ",")
24
  S = S.replace("?.", "?")
25
+ S = S.replace("??", "?")
26
+ S = S.replace(" ?", "?")
27
+ S = S.replace(" .", ".")
28
  S = S.replace(",!", "!")
29
  S = S.replace(",.", ",")
30
  S = S.replace(".]", ".")
 
56
  s = re.sub(r"\s+([.,])", r"\1", s) # remove spaces before period or comma
57
  return(s)
58
 
59
+ def getLastPage(thread_url=config.thread_url):
60
  # Returns the number of the last page
61
  page = 1 # Starting page
62
  lastPage = False
 
69
  return page
70
 
71
 
72
+ def login(username=config.username, password=config.password, thread_url=config.thread_url):
73
  # Log-In to the forum and redirect to thread
74
 
75
  # Retrieve the login page HTML to get the CSRF token
76
+ login_page_response = session.get(config.login_url)
77
  soup = BeautifulSoup(login_page_response.text, 'html.parser')
78
  csrf_token = soup.find('input', {'name': '_xfToken'})['value']
79
 
 
85
  '_xfRedirect': thread_url,
86
  '_xfToken': csrf_token
87
  }
88
+ response = session.post(config.login_url, data=login_data)
89
 
90
  # Check if the login was successful
91
  if 'Invalid login' in response.text:
92
  print('Login failed!')
93
  exit()
94
 
95
+ def post(message="", thread_url=config.thread_url, post_url=config.post_url, quoted_by="",quote_text="",quote_source=""):
96
  #Post a message to the forum (with or without the quote
97
  #quote_source is in format 'post-3920992'
98
  quote_source = quote_source.split('-')[-1] # Take the numbers only
 
129
 
130
  print('Post submitted successfully.')
131
 
132
+ def getMessages(thread_url=config.thread_url, quotedUser="", startingPage=1):
133
  # Returns all the quotes for #username in the specific multi-page thread url
134
  allquotes =[]
135
 
 
177
  if matchID:
178
  messageID = matchID.group(1)
179
 
180
+ # Match the QuotedName
181
  matchQuotedName = quotedNamePattern.search(str(data))
182
  if matchQuotedName:
183
  quotedName = matchQuotedName.group(1)
 
212
  def WarOnlineBot():
213
 
214
  lookUpPages = 5 # How many pages back to look in the thread
215
+ startingPage = getLastPage(thread_url=config.thread_url) - lookUpPages
216
  if startingPage < 1:
217
  startingPage = 1 # Starting page cannot be less than 1
218
 
219
+ login(username=config.username, password=config.password, thread_url=config.thread_url)
220
  #print("logged in")
221
 
222
  # All messages (with quotes) by ALL users:
223
+ allMessages = getMessages(thread_url=config.thread_url, quotedUser='', startingPage=startingPage)
224
 
225
  # IDs of the quoted messages, replied by the bot:
226
  messages_by_bot_IDs = []
227
 
228
  for msg in allMessages:
229
  # Set a list of replied messages IDs
230
+ if msg['messengerName'] == config.username: #message posted by the WarBot
231
  messages_by_bot_IDs.append(msg['quotedID'].split(': ')[-1])
232
  # remove empty and repeated elements
233
  messages_by_bot_IDs = list(set([elem for elem in messages_by_bot_IDs if elem]))
234
 
235
  # All messages (with quotes) sent _FOR_ the Bot:
236
+ messagesForBot = getMessages(thread_url=config.thread_url, quotedUser=config.username, startingPage=startingPage)
237
 
238
  # IDs of the messages, quoting the bot:
239
  messages_for_bot_IDs = []
 
258
  quote = remove_non_english_russian_chars(msg['reply'])
259
  quote = remove_extra_spaces(quote)
260
 
261
+ message = "" #Initiating the reply message by Bot
262
+ previous_dialogue = "" #Initiating the previous dialogue
263
 
264
  print('Quote: ', originalQuote)
265
 
266
+ # Init Connection
267
+ db = conversationDB.DataBase()
268
+ # Get the previous dialogue from the database
269
+ dbmessages = db.getmessages(msg['messengerName'])
270
+ for dbmessage in dbmessages:
271
+ previous_dialogue += dbmessage[0]+' '+dbmessage[1]+' '
272
+ # Update the string and preprocess it
273
+ quote = previous_dialogue + quote
274
+ quote = remove_non_english_russian_chars(quote)
275
+ quote = remove_extra_spaces(quote)
276
+ # Truncate the quote to return only the last MaxWords of words:
277
+ quote = " ".join(quote.split()[-config.MaxWords:])
278
+
279
+ # Fix the quote string, to eliminate errors:
280
+ quote = fixString(quote)
281
+
282
  FailureCounter = 0 # In case there is a bug in the model
283
  while (not message) and (FailureCounter<3):
284
  message = WarClient.getReply(message=quote)
 
294
  message = fixString(message)
295
  print('Reply: ', message)
296
 
297
+ # Add the new conversation pair to the database
298
+ db.setmessages(username=msg['messengerName'], message_text=originalQuote, bot_reply=message)
299
+ # Clean up the excessive records, leaving only the remaining messages
300
+ db.cleanup(username=msg['messengerName'], remaining_messages=config.remaining_messages)
301
+ # Delete the duplicate records
302
+ db.deleteDuplicates()
303
+
304
+ login(username=config.username, password=config.password, thread_url=config.thread_url)
305
  time.sleep(1)
306
+ post(message=message, thread_url=config.thread_url, post_url=config.post_url, quoted_by=msg['messengerName'], quote_text=originalQuote, quote_source=msg['messageID'])
307
 
308
  time.sleep(10) # Standby time for server load release
309
 
310
 
311
  if __name__ == '__main__':
 
312
 
313
  # Start the scheduler
314
  while True:
315
+ print('Starting Session')
316
  WarOnlineBot()
317
 
318
+ timer = range(60 * config.timeout)
319
  for t in timer:
320
  time.sleep(1)
config.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Configuration File with stored constants
2
+
3
+ # Define the login URL and the thread URL:
4
+ login_url = 'https://waronline.org/fora/index.php?login/login'
5
+ thread_url = 'https://waronline.org/fora/index.php?threads/warbot-playground.17636/'
6
+ post_url = "https://waronline.org/fora/index.php?threads/warbot-playground.17636/add-reply"
7
+
8
+ # SSH settings
9
+ ssh_host = '129.159.146.88'
10
+ ssh_user = 'ubuntu'
11
+ ssh_key_path = 'C:/Users/kerts/OneDrive/Documents/Keys/Ubuntu_Oracle/ssh-key-2023-02-12.key'
12
+ #ssh_key_path = 'ssh-key-2023-02-12.key'
13
+
14
+ # MySQL settings:
15
+ mysql_host = 'localhost' # because we will connect through the SSH tunnel
16
+ mysql_port = 3306 # the default MySQL port
17
+ mysql_user = 'root'
18
+ mysql_password = 'naP2tion'
19
+ mysql_db = 'warbot'
20
+ ssh_port = 22
21
+ dbTableName = 'conversations' # messages data table
22
+
23
+ # WarBot Server Settings:
24
+ HOST = '129.159.146.88'
25
+ PORT = 5000
26
+
27
+ # Define the login credentials:
28
+ username = 'WarBot'
29
+ password = 'naP2tion'
30
+
31
+ # Maximum number of words in message:
32
+ MaxWords = 50 # The server is relatively weak to fast-process the long messages
33
+
34
+ # Maximum conversation pairs in the Database
35
+ remaining_messages = 2
36
+
37
+ # Time between the reply sessions:
38
+ timeout = 5 # min
conversationDB.py CHANGED
@@ -1,20 +1,21 @@
1
  import paramiko
2
  import pymysql
3
  from sshtunnel import SSHTunnelForwarder
 
4
 
5
  # SSH settings
6
- ssh_host = '129.159.146.88'
7
- ssh_user = 'ubuntu'
8
- ssh_key_path = 'C:/Users/kerts/OneDrive/Documents/Keys/Ubuntu_Oracle/ssh-key-2023-02-12.key'
9
 
10
  # MySQL settings
11
- mysql_host = 'localhost' # because we will connect through the SSH tunnel
12
- mysql_port = 3306 # the default MySQL port
13
- mysql_user = 'root'
14
- mysql_password = 'naP2tion'
15
- mysql_db = 'warbot'
16
- ssh_port = 22
17
- dbTableName = 'conversations' # messages data table
18
 
19
  class DataBase():
20
  # manages mySQL connection and send-receive
@@ -92,7 +93,7 @@ class DataBase():
92
 
93
  # close the SSH client
94
  db.ssh_client.close()
95
- def cleanup(db, username = "", remaining_messages = 3):
96
  # Cleanup the records, except the last N rows
97
  with SSHTunnelForwarder(
98
  (db.ssh_host, db.ssh_port),
@@ -124,13 +125,47 @@ class DataBase():
124
  # close the SSH client
125
  db.ssh_client.close()
126
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
 
128
- if __name__ == '__main__':
129
- # This is for testing purpose only:
130
- username = 'user1'
131
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  db = DataBase()
133
  db.setmessages(username='user2',message_text='some message',bot_reply='some reply')
134
- #db.cleanup(username='user2',remaining_messages=1)
 
135
  messages = db.getmessages(username)
136
- print(messages)
 
 
 
1
  import paramiko
2
  import pymysql
3
  from sshtunnel import SSHTunnelForwarder
4
+ import config # Here the constants are stored
5
 
6
  # SSH settings
7
+ ssh_host = config.ssh_host
8
+ ssh_user = config.ssh_user
9
+ ssh_key_path = config.ssh_key_path
10
 
11
  # MySQL settings
12
+ mysql_host = config.mysql_host
13
+ mysql_port = config.mysql_port
14
+ mysql_user = config.mysql_user
15
+ mysql_password = config.mysql_password
16
+ mysql_db = config.mysql_db
17
+ ssh_port = config.ssh_port
18
+ dbTableName = config.dbTableName
19
 
20
  class DataBase():
21
  # manages mySQL connection and send-receive
 
93
 
94
  # close the SSH client
95
  db.ssh_client.close()
96
+ def cleanup(db, username = "", remaining_messages = config.remaining_messages):
97
  # Cleanup the records, except the last N rows
98
  with SSHTunnelForwarder(
99
  (db.ssh_host, db.ssh_port),
 
125
  # close the SSH client
126
  db.ssh_client.close()
127
 
128
+ def deleteDuplicates(db):
129
+ # Deletes all the duplicate records
130
+ with SSHTunnelForwarder(
131
+ (db.ssh_host, db.ssh_port),
132
+ ssh_username=db.ssh_user,
133
+ ssh_pkey=db.private_key,
134
+ remote_bind_address=(db.mysql_host, db.mysql_port)) as tunnel:
135
+ # connect to the MySQL server through the SSH tunnel
136
+ mysql_conn = pymysql.connect(
137
+ host='localhost', # will be due to the SSH tunneling issue
138
+ port=tunnel.local_bind_port,
139
+ user=db.mysql_user,
140
+ password=db.mysql_password,
141
+ db=db.mysql_db
142
+ )
143
 
144
+ # send a query to the MySQL database to delete the records except the last ones
145
+ with mysql_conn.cursor() as cursor:
 
146
 
147
+ query = f"DELETE c1 FROM {dbTableName} c1, {dbTableName} c2 WHERE " \
148
+ f"c1.id > c2.id AND c1.username = c2.username " \
149
+ f"AND c1.message_text = c2.message_text;"
150
+ cursor.execute(query)
151
+ mysql_conn.commit()
152
+
153
+ # close the MySQL connection
154
+ mysql_conn.close()
155
+
156
+ # close the SSH client
157
+ db.ssh_client.close()
158
+
159
+ if __name__ == '__main__':
160
+ pass
161
+ """
162
+ #This is for testing purpose only:
163
+ username = 'u1'
164
  db = DataBase()
165
  db.setmessages(username='user2',message_text='some message',bot_reply='some reply')
166
+ db.cleanup(username='user2',remaining_messages=1)
167
+ db.deleteDuplicates()
168
  messages = db.getmessages(username)
169
+ for message in messages:
170
+ print(message[0]+' '+message[0]+' ')
171
+ """