Skip to content

Commit b06d6eb

Browse files
authored
Merge pull request CloudBotIRC#151 from linuxdaemon/gonzobot+duck-hunt-error-handling
Handle database errors during duckhunt.befriend and duckhunt.bang
2 parents 6595c14 + 84ff78c commit b06d6eb

1 file changed

Lines changed: 80 additions & 97 deletions

File tree

plugins/duckhunt.py

Lines changed: 80 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
from time import time
55

66
from sqlalchemy import Table, Column, String, Integer, PrimaryKeyConstraint, desc, Boolean
7+
from sqlalchemy.exc import DatabaseError
78
from sqlalchemy.sql import select
89

910
from cloudbot import hook
1011
from cloudbot.event import EventType
1112
from cloudbot.util import database
13+
from cloudbot.util.formatting import pluralize
1214

1315
duck_tail = "・゜゜・。。・゜゜"
1416
duck = ["\_o< ", "\_O< ", "\_0< ", "\_\u00f6< ", "\_\u00f8< ", "\_\u00f3< "]
@@ -165,7 +167,7 @@ def stop_hunt(chan, conn):
165167

166168

167169
@hook.command("duckkick", permissions=["chanop", "op", "botcontrol"])
168-
def no_duck_kick(text, chan, conn, notice):
170+
def no_duck_kick(text, chan, conn, notice_doc):
169171
"""<enable|disable> - If the bot has OP or half-op in the channel you can specify .duckkick enable|disable so that people are kicked for shooting or befriending a non-existent goose. Default is off."""
170172
global game_status
171173
if chan in opt_out:
@@ -177,7 +179,7 @@ def no_duck_kick(text, chan, conn, notice):
177179
game_status[conn.name][chan]['no_duck_kick'] = 0
178180
return "kicking for non-existent ducks has been disabled."
179181
else:
180-
notice(no_duck_kick.__doc__)
182+
notice_doc()
181183
return
182184

183185

@@ -191,7 +193,7 @@ def generate_duck():
191193
dnoise = random.choice(duck_noise)
192194
rn = random.randint(1, len(dnoise) - 1)
193195
dnoise = dnoise[:rn] + u'\u200b' + dnoise[rn:]
194-
return (dtail, dbody, dnoise)
196+
return dtail, dbody, dnoise
195197

196198

197199
@hook.periodic(11, initial_interval=11)
@@ -216,11 +218,6 @@ def deploy_duck(bot):
216218
game_status[network][chan]['duck_time'] = time()
217219
dtail, dbody, dnoise = generate_duck()
218220
conn.message(chan, "{}{}{}".format(dtail, dbody, dnoise))
219-
# Leave this commented out for now. I haven't decided how to make ducks leave.
220-
# if active == 1 and duck_status == 1 and game_status[network][chan]['flyaway'] <= int(time()):
221-
# conn.message(chan, "The duck flew away.")
222-
# game_status[network][chan]['duck_status'] = 2
223-
# set_ducktime(chan, conn)
224221
continue
225222
continue
226223

@@ -277,127 +274,113 @@ def dbupdate(nick, chan, db, conn, shoot, friend):
277274
db.commit()
278275

279276

280-
@hook.command("bang", autohelp=False)
281-
def bang(nick, chan, message, db, conn, notice):
282-
"""- when there is a duck on the loose use this command to shoot it."""
283-
global game_status, scripters
284-
if chan in opt_out:
285-
return
286-
network = conn.name
287-
out = ""
288-
miss = ["WHOOSH! You missed the duck completely!", "Your gun jammed!", "Better luck next time.",
289-
"WTF?! Who are you, Kim Jong Un firing missiles? You missed."]
290-
if not game_status[network][chan]['game_on']:
291-
return "There is no activehunt right now. Use .starthunt to start a game."
292-
elif game_status[network][chan]['duck_status'] != 1:
293-
if game_status[network][chan]['no_duck_kick'] == 1:
294-
out = "KICK {} {} :There is no duck! What are you shooting at?".format(chan, nick)
295-
conn.send(out)
296-
return
297-
return "There is no duck. What are you shooting at?"
277+
def update_score(nick, chan, db, conn, shoot=0, friend=0):
278+
score = db.execute(select([table.c.shot, table.c.befriend])
279+
.where(table.c.network == conn.name)
280+
.where(table.c.chan == chan.lower())
281+
.where(table.c.name == nick.lower())).fetchone()
282+
if score:
283+
score = score[0]
284+
score += 1
285+
dbupdate(nick, chan, db, conn, score[0] + shoot, score[1] + friend)
298286
else:
299-
game_status[network][chan]['shoot_time'] = time()
300-
deploy = game_status[network][chan]['duck_time']
301-
shoot = game_status[network][chan]['shoot_time']
302-
if nick.lower() in scripters:
303-
if scripters[nick.lower()] > shoot:
304-
notice("You are in a cool down period, you can try again in {} seconds.".format(
305-
str(scripters[nick.lower()] - shoot)))
306-
return
307-
chance = hit_or_miss(deploy, shoot)
308-
if not random.random() <= chance and chance > .05:
309-
out = random.choice(miss) + " You can try again in 7 seconds."
310-
scripters[nick.lower()] = shoot + 7
311-
return out
312-
if chance == .05:
313-
out += "You pulled the trigger in {} seconds, that's mighty fast. Are you sure you aren't a script? Take a 2 hour cool down.".format(
314-
str(shoot - deploy))
315-
scripters[nick.lower()] = shoot + 7200
316-
if not random.random() <= chance:
317-
return random.choice(miss) + " " + out
318-
else:
319-
message(out)
320-
game_status[network][chan]['duck_status'] = 2
321-
score = db.execute(select([table.c.shot]) \
322-
.where(table.c.network == conn.name) \
323-
.where(table.c.chan == chan.lower()) \
324-
.where(table.c.name == nick.lower())).fetchone()
325-
if score:
326-
score = score[0]
327-
score += 1
328-
dbupdate(nick, chan, db, conn, score, 0)
329-
else:
330-
score = 1
331-
dbadd_entry(nick, chan, db, conn, score, 0)
332-
timer = "{:.3f}".format(shoot - deploy)
333-
duck = "duck" if score == 1 else "ducks"
334-
message("{} you shot a duck in {} seconds! You have killed {} {} in {}.".format(nick, timer, score, duck, chan))
335-
set_ducktime(chan, conn.name)
287+
dbadd_entry(nick, chan, db, conn, shoot, friend)
336288

337289

338-
@hook.command("befriend", autohelp=False)
339-
def befriend(nick, chan, message, db, conn, notice):
340-
"""- when there is a duck on the loose use this command to befriend it before someone else shoots it."""
290+
def attack(nick, chan, message, db, conn, notice, attack):
341291
global game_status, scripters
342292
if chan in opt_out:
343293
return
294+
344295
network = conn.name
296+
status = game_status[network][chan]
297+
345298
out = ""
346-
miss = ["The duck didn't want to be friends, maybe next time.",
299+
if attack == "shoot":
300+
miss = [
301+
"WHOOSH! You missed the duck completely!", "Your gun jammed!",
302+
"Better luck next time.",
303+
"WTF?! Who are you, Kim Jong Un firing missiles? You missed."
304+
]
305+
no_duck = "There is no duck! What are you shooting at?"
306+
msg = "{} you shot a duck in {:.3f} seconds! You have killed {} in {}."
307+
scripter_msg = "You pulled the trigger in {:.3f} seconds, that's mighty fast. Are you sure you aren't a script? Take a 2 hour cool down."
308+
attack_type = "shoot"
309+
else:
310+
miss = [
311+
"The duck didn't want to be friends, maybe next time.",
347312
"Well this is awkward, the duck needs to think about it.",
348313
"The duck said no, maybe bribe it with some pizza? Ducks love pizza don't they?",
349-
"Who knew ducks could be so picky?"]
350-
if not game_status[network][chan]['game_on']:
314+
"Who knew ducks could be so picky?"
315+
]
316+
no_duck = "You tried befriending a non-existent duck. That's freaking creepy."
317+
msg = "{} you befriended a duck in {:.3f} seconds! You have made friends with {} in {}."
318+
scripter_msg = "You tried friending that duck in {:.3f} seconds, that's mighty fast. Are you sure you aren't a script? Take a 2 hour cool down."
319+
attack_type = "friend"
320+
321+
if not status['game_on']:
351322
return "There is no hunt right now. Use .starthunt to start a game."
352-
elif game_status[network][chan]['duck_status'] != 1:
353-
if game_status[network][chan]['no_duck_kick'] == 1:
354-
out = "KICK {} {} :You tried befriending a non-existent duck. That's fucking creepy.".format(chan, nick)
355-
conn.send(out)
323+
elif status['duck_status'] != 1:
324+
if status['no_duck_kick'] == 1:
325+
conn.cmd("KICK", chan, nick, no_duck)
356326
return
357-
return "You tried befriending a non-existent duck. That's freaking creepy."
327+
328+
return no_duck
358329
else:
359-
game_status[network][chan]['shoot_time'] = time()
360-
deploy = game_status[network][chan]['duck_time']
361-
shoot = game_status[network][chan]['shoot_time']
330+
status['shoot_time'] = time()
331+
deploy = status['duck_time']
332+
shoot = status['shoot_time']
362333
if nick.lower() in scripters:
363334
if scripters[nick.lower()] > shoot:
364-
notice("You are in a cool down period, you can try again in {} seconds.".format(
365-
str(scripters[nick.lower()] - shoot)))
335+
notice(
336+
"You are in a cool down period, you can try again in {:.3f} seconds.".format(
337+
scripters[nick.lower()] - shoot
338+
)
339+
)
366340
return
341+
367342
chance = hit_or_miss(deploy, shoot)
368343
if not random.random() <= chance and chance > .05:
369344
out = random.choice(miss) + " You can try again in 7 seconds."
370345
scripters[nick.lower()] = shoot + 7
371346
return out
347+
372348
if chance == .05:
373-
out += "You tried friending that duck in {} seconds, that's mighty fast. Are you sure you aren't a script? Take a 2 hour cool down.".format(
374-
str(shoot - deploy))
349+
out += scripter_msg.format(shoot - deploy)
375350
scripters[nick.lower()] = shoot + 7200
376351
if not random.random() <= chance:
377352
return random.choice(miss) + " " + out
378353
else:
379354
message(out)
380355

381-
game_status[network][chan]['duck_status'] = 2
382-
score = db.execute(select([table.c.befriend]) \
383-
.where(table.c.network == conn.name) \
384-
.where(table.c.chan == chan.lower()) \
385-
.where(table.c.name == nick.lower())).fetchone()
386-
if score:
387-
score = score[0]
388-
score += 1
389-
dbupdate(nick, chan, db, conn, 0, score)
390-
else:
356+
status['duck_status'] = 2
357+
try:
391358
score = 1
392-
dbadd_entry(nick, chan, db, conn, 0, score)
393-
duck = "duck" if score == 1 else "ducks"
394-
timer = "{:.3f}".format(shoot - deploy)
395-
message(
396-
"{} you befriended a duck in {} seconds! You have made friends with {} {} in {}.".format(nick, timer, score,
397-
duck, chan))
359+
args = {
360+
attack_type: score
361+
}
362+
363+
update_score(nick, chan, db, conn, **args)
364+
except DatabaseError:
365+
status['duck_status'] = 1
366+
raise
367+
368+
message(msg.format(nick, shoot - deploy, pluralize(score, "duck"), chan))
398369
set_ducktime(chan, conn.name)
399370

400371

372+
@hook.command("bang", autohelp=False)
373+
def bang(nick, chan, message, db, conn, notice):
374+
"""- when there is a duck on the loose use this command to shoot it."""
375+
return attack(nick, chan, message, db, conn, notice, "shoot")
376+
377+
378+
@hook.command("befriend", autohelp=False)
379+
def befriend(nick, chan, message, db, conn, notice):
380+
"""- when there is a duck on the loose use this command to befriend it before someone else shoots it."""
381+
return attack(nick, chan, message, db, conn, notice, "befriend")
382+
383+
401384
def smart_truncate(content, length=320, suffix='...'):
402385
if len(content) <= length:
403386
return content

0 commit comments

Comments
 (0)