This repository was archived by the owner on Sep 30, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbots.py
More file actions
302 lines (226 loc) · 10.2 KB
/
bots.py
File metadata and controls
302 lines (226 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
import responses
import random
import re
# Reactions
u = 'neutral'
p = 'positive'
n = 'negative'
r = [u, p, n]
debug = False
# Placeholder that is replaced with an action suggested by other clients
keyword_to_replace1 = kwtr1 = responses.kwtr1
# Placeholder that is replaced with an action suggested by the current bot
keyword_to_replace2 = kwtr2 = responses.kwtr2
# Placeholder that is replaced with an action suggested by the host
keyword_to_replace3 = kwtr3 = responses.kwtr3
# Actions suggested by other clients
suggested_actions = []
# New set of actions suggested by the current bot
new_actions = []
# Alias of a bot
alias = ''
# var choices = actions the bot can suggest,
# manipulated so that the current bot doesn't suggest the same action as the previous suggested action
# List manipulation necessary because all 'bots' are sharing the same list of actions (see responses.py -> bot_actions[])
choices = [x for x in responses.bot_actions]
# The bot() function can be reused
# You can create multiple clients with this bot
# as long as the alias given is not already used by other clients
# There are 3 predefined 'bots' [steven, arthur, andrea]
# As long as these predefined aliases are available,
# when running client.py and the bot option is not specified,
# a bot is connected automatically with the available predefined alias
# If all bots are all in use, the user is asked if they want to still connect
# if yes, a bot name is requested
# If no, their connection is terminated
# The host can also use this bot
# however it doesn't use the same dialogs as the regular bots (see responses.py)
def bot(bot_name, actions, reaction):
global suggested_actions, alias
suggested_actions = actions
alias = bot_name
reply = ''
# Shows which actions are extracted from previous reply
if alias != 'Host' and debug:
print(f'\t-> Suggested action(s) found from previous reply: {suggested_actions}')
# These extracted actions are temporarily removed so that
# the current bot don't suggest it again
for a in suggested_actions:
if a in choices:
choices.remove(a)
if debug:
print(f"\t-> [{a}] is temporarily removed from actions to prevent bot from suggesting the same action.")
print(f"\t-> Bot can still agree to action: [{a}]")
# Shows which actions the current bot can suggest
# if alias != 'Host' and debug:
# print(f'\t-> Action(s) left to suggest: {choices}')
# The bot response is formulated
if len(actions) > 0:
reply += generate_reply(reaction)
else:
return format_replies(alias, "I don't know how to respond to that.", actions, random.choice(r))
# Shows which actions are used in the formulated sentence
if alias != 'Host' and debug:
print(f'\t-> Bot used this action(s) in its sentence: {new_actions}\n')
# Just makes the host name more dynamic
if alias == 'Host':
alias = random.choice(responses.host_identities)
# Returns the formulated sentence
return format_replies(alias, reply, new_actions, random.choice(r))
# Formats the bot reply to a valid json format
def format_replies(sender, message, actions, reaction):
return {
"sender": sender,
"message": message,
"actions": actions,
"reaction": reaction
}
# generate_reply() = bots generate replies coming from responses.py
# depending on the expected reaction and depending who the bot is
# the host has a different set of responses,
# rather they're suggestions that initiates the conversation between the regular bots
def generate_reply(reaction):
# By clearing the new_actions list,
# it uses only actions mentioned from the previous reply
new_actions.clear()
# If the bot is the host, use the host suggestion responses (see responses.py)
if alias == 'Host':
suggestion = random.choice(responses.suggestions)
return formulate_sentence(suggestion, reaction)
# If the bot is a regular client, use the client replies (see responses.py)
else:
if reaction == p:
sentence = random.choice(responses.positive_replies)
elif reaction == n:
sentence = random.choice(responses.negative_replies)
else:
sentence = random.choice(responses.neutral_replies)
return formulate_sentence(sentence, reaction)
def formulate_sentence(sentence, reaction):
global choices
# These are placeholder counters
# With the help of these, all placeholder in a sentence are replaced with a random action
count_keyword_1 = count(sentence, keyword_to_replace1)
count_keyword_2 = count(sentence, keyword_to_replace2)
count_keyword_3 = count(sentence, keyword_to_replace3)
# Length of suggested action list
number_of_suggested_actions = len(suggested_actions)
# If host keyword is greater than 0, formulate the sentence
# This occurs only once, it's when the server starts
if count_keyword_3 > 0:
sentence = replace_placeholder(sentence, keyword_to_replace3, count_keyword_3)
else:
# Number of suggested actions must be greater than the number of placeholders
if count_keyword_1 > number_of_suggested_actions:
# While the number of placeholder is greater than the number of suggested actions,
# generate another sentence that can accommodate the number of suggested actions
while count_keyword_1 > number_of_suggested_actions:
sentence = generate_reply(reaction)
count_keyword_1 = count(sentence, keyword_to_replace1)
if number_of_suggested_actions >= count_keyword_1:
break
return replace_placeholder(sentence, keyword_to_replace1, count_keyword_1)
# if the number of actions is enough to replace the number of placeholders
else:
if count_keyword_1 > 0:
sentence = replace_placeholder(sentence, keyword_to_replace1, count_keyword_1)
if count_keyword_2 > 0:
# new_actions.clear() = used so that all actions from previous reply is ignored
# except for the new suggested actions
new_actions.clear()
sentence = replace_placeholder(sentence, keyword_to_replace2, count_keyword_2)
if debug:
print(f'\t-> Action(s) left to suggest: {choices}')
choices.clear()
choices = [x for x in responses.bot_actions]
return sentence
# Generates actions from the host actions list (responses.py)
def suggest_host_action():
action = responses.host_actions.pop(random.choice(range(len(responses.host_actions))))
if action not in new_actions:
new_actions.append(action)
return action
# Generates actions from the bots actions list (responses.py)
def suggest_new_action(array):
action = array.pop(random.choice(range(len(array))))
if debug:
print(f'\t-> New action the bot want to suggest: {action}')
if action not in new_actions:
new_actions.append(action)
return action
# Generates actions suggested by other clients
def get_action():
global choices
action = suggested_actions.pop(random.choice(range(len(suggested_actions))))
if action not in new_actions:
new_actions.append(action)
return action
# Replies from responses.py uses placeholders (kwtr1 and kwtr2) for actions
# This method replaces those placeholders with valid actions
def replace_placeholder(sentence, keyword, limit):
# If placeholder replacement needs to be from extracted actions
# (actions suggested by other clients)
if keyword == kwtr1:
for i in range(limit):
action = get_action()
sentence = re.sub(kwtr1, action, sentence, 1)
sentence = grammar_fixer(sentence, action)
# If placeholder replacement needs to be from bots actions
# (actions this client want to suggest)
elif keyword == kwtr2:
for i in range(limit):
action = suggest_new_action(choices)
sentence = re.sub(kwtr2, action, sentence, 1)
sentence = grammar_fixer(sentence, action)
# If placeholder replacement needs to be from host actions
# (actions this client want to suggest)
elif keyword == kwtr3:
for i in range(limit):
action = suggest_host_action()
sentence = re.sub(kwtr3, action, sentence, 1)
sentence = grammar_fixer(sentence, action)
return sentence
# Fixes the form of actions
def grammar_fixer(sentence, action):
ing_counter = count(sentence, (action + 'ing'))
if ing_counter > 0:
# F.eks action ends with an 'e' but
# the sentence needs it to end with an 'ing'
if str(action).endswith('e'):
# If action = 'drive'
# then a2 = 'driv'
# action becomes 'driving' in the sentence
a2 = str(action).rsplit('e', 1)[0]
sentence = re.sub(action, a2, sentence, 1)
# F.eks action needs double letters when changed to an ing action
# F.eks rob becomes robbing
# (b needs to be doubled)
# @d indicates that the action needs a double letter
if str(action).endswith('@d'):
# If action = 'rob@d'
# then a2 = 'rob'
# and last_char = 'b'
# then a3 = 'robb'
# action becomes 'robbing' in the sentence
a2 = str(action).rsplit('@d', 1)[0]
last_char = a2[len(a2) - 1:]
a3 = str(a2 + last_char)
sentence = re.sub(action, a3, sentence, 1)
else:
# If action does not need to change to an ing action
# but has the @d, remove the @d
try:
a2 = str(action).rsplit('@d', 1)[0]
sentence = re.sub(action, a2, sentence, 1)
# If action does not need to change to an ing action
# and there are no placeholders, send the sentence back
except:
# When no grammar is needed to be fixed
return sentence
return sentence
# Counts how many placeholders needs to be replaced
def count(sentence, placeholder):
counter = 0
for _ in re.finditer(placeholder, sentence):
counter += 1
return counter