Browse Source

Make Reaction.users return an async iterator.

pull/468/head
Rapptz 8 years ago
parent
commit
ae6fb54b1b
  1. 75
      discord/iterators.py
  2. 57
      discord/reaction.py

75
discord/iterators.py

@ -35,6 +35,81 @@ from .object import Object
PY35 = sys.version_info >= (3, 5)
class ReactionIterator:
def __init__(self, message, emoji, limit=100, after=None):
self.message = message
self.limit = limit
self.after = after
state = message._state
self.getter = state.http.get_reaction_users
self.state = state
self.emoji = emoji
self.guild = message.guild
self.channel_id = message.channel.id
self.users = asyncio.Queue(loop=state.loop)
@asyncio.coroutine
def get(self):
if self.users.empty():
yield from self.fill_users()
try:
return self.users.get_nowait()
except asyncio.QueueEmpty:
raise NoMoreItems()
@asyncio.coroutine
def fill_users(self):
# this is a hack because >circular imports<
from .user import User
if self.limit > 0:
retrieve = self.limit if self.limit <= 100 else 100
after = self.after.id if self.after else None
data = yield from self.getter(self.message.id, self.channel_id, self.emoji, retrieve, after=after)
if data:
self.limit -= retrieve
self.after = Object(id=int(data[0]['id']))
if self.guild is None:
for element in reversed(data):
yield from self.users.put(User(state=self.state, data=element))
else:
for element in reversed(data):
member_id = int(element['id'])
member = self.guild.get_member(member_id)
if member is not None:
yield from self.users.put(member)
else:
yield from self.users.put(User(state=self.state, data=element))
@asyncio.coroutine
def flatten(self):
ret = []
while True:
try:
user = yield from self.get()
except NoMoreItems:
return ret
else:
ret.append(user)
if PY35:
@asyncio.coroutine
def __aiter__(self):
return self
@asyncio.coroutine
def __anext__(self):
try:
msg = yield from self.get()
except NoMoreItems:
raise StopAsyncIteration()
else:
return msg
class HistoryIterator:
"""Iterator for receiving a channel's message history.

57
discord/reaction.py

@ -26,7 +26,7 @@ DEALINGS IN THE SOFTWARE.
import asyncio
from .user import User
from .iterators import ReactionIterator
class Reaction:
"""Represents a reaction to a message.
@ -86,11 +86,11 @@ class Reaction:
def __repr__(self):
return '<Reaction emoji={0.emoji!r} me={0.me} count={0.count}>'.format(self)
@asyncio.coroutine
def users(self, limit=100, after=None):
def users(self, limit=None, after=None):
"""|coro|
Get the users that added this reaction.
Returns an asynchronous iterator representing the
users that have reacted to the message.
The ``after`` parameter must represent a member
and meet the :class:`abc.Snowflake` abc.
@ -99,6 +99,8 @@ class Reaction:
------------
limit: int
The maximum number of results to return.
If not provided, returns all the users who
reacted to the message.
after: :class:`abc.Snowflake`
For pagination, reactions are sorted by member.
@ -107,23 +109,48 @@ class Reaction:
HTTPException
Getting the users for the reaction failed.
Returns
Examples
---------
Usage ::
# I do not actually recommend doing this.
async for user in reaction.users():
await channel.send('{0} has reacted with {1.emoji}!'.format(user, reaction))
Flattening into a list: ::
users = await reaction.users().flatten()
# users is now a list...
winner = random.choice(users)
await channel.send('{} has won the raffle.'.format(winner))
Python 3.4 Usage ::
iterator = reaction.users()
while True:
try:
user = yield from iterator.get()
except discord.NoMoreItems:
break
else:
await channel.send('{0} has reacted with {1.emoji}!'.format(user, reaction))
Yields
--------
List[:class:`User`]
A list of users who reacted to the message.
Union[:class:`User`, :class:`Member`]
The member (if retrievable) or the user that has reacted
to this message. The case where it can be a :class:`Member` is
in a guild message context. Sometimes it can be a :class:`User`
if the member has left the guild.
"""
# TODO: Return an iterator a la `Messageable.history`?
if self.custom_emoji:
emoji = '{0.name}:{0.id}'.format(self.emoji)
else:
emoji = self.emoji
if after:
after = after.id
if limit is None:
limit = self.count
msg = self.message
state = msg._state
data = yield from state.http.get_reaction_users(msg.id, msg.channel.id, emoji, limit, after=after)
return [User(state=state, data=user) for user in data]
return ReactionIterator(self.message, emoji, limit, after)

Loading…
Cancel
Save