More listener

pull/147/head
bobloy 4 years ago
parent 28bf2a73e1
commit 1723dc381d

@ -1,4 +1,5 @@
import asyncio
import inspect
import logging
import random
from collections import deque
@ -36,9 +37,7 @@ class Game:
"**Morning has arrived on day {}..**",
]
night_messages = [
"**Dawn falls on day {}..****"
]
night_messages = ["**Dawn falls on day {}..****"]
day_vote_count = 3
@ -87,6 +86,7 @@ class Game:
self.loop = asyncio.get_event_loop()
self.action_queue = deque()
self.listeners = {}
# def __del__(self):
# """
@ -265,9 +265,7 @@ class Game:
############START Notify structure############
async def _cycle(self):
"""
Each event calls the next event
Each event enqueues the next event
_at_day_start()
_at_voted()
@ -296,7 +294,7 @@ class Game:
embed=discord.Embed(title="Game is starting, please wait for setup to complete")
)
await self._notify(0)
await self._notify("at_game_start")
async def _at_day_start(self): # ID 1
if self.game_over:
@ -318,7 +316,7 @@ class Game:
await self.generate_targets(self.village_channel)
await self.day_perms(self.village_channel)
await self._notify(1)
await self._notify("at_day_start")
await self._check_game_over()
if self.game_over:
@ -346,7 +344,7 @@ class Game:
if self.game_over:
return
data = {"player": target}
await self._notify(2, data)
await self._notify("at_voted", player=target)
self.ongoing_vote = True
@ -357,9 +355,7 @@ class Game:
"**{} will be put to trial and has 30 seconds to defend themselves**".format(
target.mention
),
allowed_mentions=discord.AllowedMentions(
everyone=False, users=[target]
)
allowed_mentions=discord.AllowedMentions(everyone=False, users=[target]),
)
await asyncio.sleep(30)
@ -371,9 +367,7 @@ class Game:
"👍 to save, 👎 to lynch\n"
"*Majority rules, no-lynch on ties, "
"vote both or neither to abstain, 15 seconds to vote*".format(target.mention),
allowed_mentions=discord.AllowedMentions(
everyone=False, users=[target]
)
allowed_mentions=discord.AllowedMentions(everyone=False, users=[target]),
)
await message.add_reaction("👍")
@ -422,13 +416,13 @@ class Game:
if self.game_over:
return
data = {"player": target}
await self._notify(3, data)
await self._notify("at_kill", player=target)
async def _at_hang(self, target): # ID 4
if self.game_over:
return
data = {"player": target}
await self._notify(4, data)
await self._notify("at_hang", player=target)
async def _at_day_end(self): # ID 5
await self._check_game_over()
@ -447,14 +441,14 @@ class Game:
embed=discord.Embed(title="**The sun sets on the village...**")
)
await self._notify(5)
await self._notify("at_day_end")
await asyncio.sleep(5)
self.action_queue.append(self._at_night_start())
async def _at_night_start(self): # ID 6
if self.game_over:
return
await self._notify(6)
await self._notify("at_night_start")
await asyncio.sleep(12) # 2 minutes FixMe to 120 later
await self.village_channel.send(
@ -471,7 +465,7 @@ class Game:
async def _at_night_end(self): # ID 7
if self.game_over:
return
await self._notify(7)
await self._notify("at_night_end")
await asyncio.sleep(10)
self.action_queue.append(self._at_day_start())
@ -480,25 +474,30 @@ class Game:
if self.game_over:
return
data = {"target": target, "source": source}
await self._notify(8, data)
await self._notify("at_visit", target=target, source=source)
async def _notify(self, event, data=None):
async def _notify(self, event, **kwargs):
for i in range(1, 7): # action guide 1-6 (0 is no action)
tasks = []
for event in self.listeners.get(event, []):
tasks.append(asyncio.ensure_future(event(**kwargs), loop=self.loop))
await asyncio.gather(*tasks)
# self.bot.dispatch(f"red.fox.werewolf.{event}", data=data, priority=i)
# self.bot.extra_events
tasks = []
# Role priorities
role_order = [role for role in self.roles if role.action_list[event][1] == i]
for role in role_order:
tasks.append(asyncio.ensure_future(role.on_event(event, data), loop=self.loop))
# VoteGroup priorities
vote_order = [vg for vg in self.vote_groups.values() if vg.action_list[event][1] == i]
for vote_group in vote_order:
tasks.append(
asyncio.ensure_future(vote_group.on_event(event, data), loop=self.loop)
)
if tasks:
await asyncio.gather(*tasks)
# tasks = []
# # Role priorities
# role_order = [role for role in self.roles if role.action_list[event][1] == i]
# for role in role_order:
# tasks.append(asyncio.ensure_future(role.on_event(event, data), loop=self.loop))
# # VoteGroup priorities
# vote_order = [vg for vg in self.vote_groups.values() if vg.action_list[event][1] == i]
# for vote_group in vote_order:
# tasks.append(
# asyncio.ensure_future(vote_group.on_event(event, data), loop=self.loop)
# )
# if tasks:
# await asyncio.gather(*tasks)
# Run same-priority task simultaneously
############END Notify structure############
@ -911,3 +910,117 @@ class Game:
pass
# Optional dynamic channels/categories
@classmethod
def wolflistener(cls, name=None):
"""A decorator that marks a function as a listener.
This is the cog equivalent of :meth:`.Bot.listen`.
Parameters
------------
name: :class:`str`
The name of the event being listened to. If not provided, it
defaults to the function's name.
Raises
--------
TypeError
The function is not a coroutine function or a string was not passed as
the name.
"""
if name is not None and not isinstance(name, str):
raise TypeError(
"Cog.listener expected str but received {0.__class__.__name__!r} instead.".format(
name
)
)
def decorator(func):
actual = func
if isinstance(actual, staticmethod):
actual = actual.__func__
if not inspect.iscoroutinefunction(actual):
raise TypeError("Listener function must be a coroutine function.")
actual.__werewolf_listener__ = True
to_assign = name or actual.__name__
try:
actual.__cog_listener_names__.append(to_assign)
except AttributeError:
actual.__cog_listener_names__ = [to_assign]
# we have to return `func` instead of `actual` because
# we need the type to be `staticmethod` for the metaclass
# to pick it up but the metaclass unfurls the function and
# thus the assignments need to be on the actual function
return func
return decorator
def wolflisten(self, name=None):
"""A decorator that registers another function as an external
event listener. Basically this allows you to listen to multiple
events from different places e.g. such as :func:`.on_ready`
The functions being listened to must be a :ref:`coroutine <coroutine>`.
Example
--------
.. code-block:: python3
@bot.listen()
async def on_message(message):
print('one')
# in some other file...
@bot.listen('on_message')
async def my_message(message):
print('two')
Would print one and two in an unspecified order.
Raises
-------
TypeError
The function being listened to is not a coroutine.
"""
def decorator(func):
self.add_wolflistener(func, name)
return func
return decorator
def add_wolflistener(self, func, name=None):
"""The non decorator alternative to :meth:`.listen`.
Parameters
-----------
func: :ref:`coroutine <coroutine>`
The function to call.
name: Optional[:class:`str`]
The name of the event to listen for. Defaults to ``func.__name__``.
Example
--------
.. code-block:: python3
async def on_ready(): pass
async def my_message(message): pass
bot.add_listener(on_ready)
bot.add_listener(my_message, 'on_message')
"""
name = func.__name__ if name is None else name
if not asyncio.iscoroutinefunction(func):
raise TypeError('Listeners must be coroutines')
if name in self.listeners:
self.listeners[name].append(func)
else:
self.listeners[name] = [func]

@ -1,5 +1,8 @@
import inspect
import logging
from werewolf import Werewolf
log = logging.getLogger("red.fox_v3.werewolf.role")
@ -64,27 +67,27 @@ class Role:
self.blocked = False
self.properties = {} # Extra data for other roles (i.e. arsonist)
self.action_list = [
(self._at_game_start, 1), # (Action, Priority)
(self._at_day_start, 0),
(self._at_voted, 0),
(self._at_kill, 0),
(self._at_hang, 0),
(self._at_day_end, 0),
(self._at_night_start, 0),
(self._at_night_end, 0),
(self._at_visit, 0),
]
# self.action_list = [
# (self._at_game_start, 1), # (Action, Priority)
# (self._at_day_start, 0),
# (self._at_voted, 0),
# (self._at_kill, 0),
# (self._at_hang, 0),
# (self._at_day_end, 0),
# (self._at_night_start, 0),
# (self._at_night_end, 0),
# (self._at_visit, 0),
# ]
def __repr__(self):
return self.__class__.__name__
async def on_event(self, event, data):
"""
See Game class for event guide
"""
await self.action_list[event][0](data)
# async def on_event(self, event, data):
# """
# See Game class for event guide
# """
#
# await self.action_list[event][0](data)
async def assign_player(self, player):
"""
@ -124,35 +127,36 @@ class Role:
"""
return "Default"
@wolflistener("at_game_start")
async def _at_game_start(self, data=None):
if self.channel_id:
await self.game.register_channel(self.channel_id, self)
await self.player.send_dm(self.game_start_message) # Maybe embeds eventually
async def _at_day_start(self, data=None):
pass
async def _at_voted(self, data=None):
pass
async def _at_kill(self, data=None):
pass
async def _at_hang(self, data=None):
pass
async def _at_day_end(self, data=None):
pass
async def _at_night_start(self, data=None):
pass
async def _at_night_end(self, data=None):
pass
async def _at_visit(self, data=None):
pass
# async def _at_day_start(self, data=None):
# pass
#
# async def _at_voted(self, data=None):
# pass
#
# async def _at_kill(self, data=None):
# pass
#
# async def _at_hang(self, data=None):
# pass
#
# async def _at_day_end(self, data=None):
# pass
#
# async def _at_night_start(self, data=None):
# pass
#
# async def _at_night_end(self, data=None):
# pass
#
# async def _at_visit(self, data=None):
# pass
async def kill(self, source):
"""

Loading…
Cancel
Save