GithubHelp home page GithubHelp logo

shayypy / guilded.py Goto Github PK

View Code? Open in Web Editor NEW
133.0 7.0 25.0 1.93 MB

Asynchronous Guilded API wrapper for Python

Home Page: https://guildedpy.rtfd.io

License: Other

Python 100.00%
python guilded guilded-api guilded-bot aiohttp

guilded.py's People

Contributors

8r2y5 avatar anytarseir67 avatar henry232323 avatar julien777z avatar madonuko avatar mikeshardmind avatar shayypy avatar thomasjryan avatar wxmichael avatar yumyummity avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

guilded.py's Issues

Event triggers

So, I was making an anti-nuke and anti-raid bot, but it seems like the events don't provide information about the person who triggered the event, which means I can't implement features like whitelisting, etc.

Code Block causes 500 Error

Describe the bug
Bot sends a message to channel that contains:

```
Words and stuff
```

Causes 500 Error

Expected behavior
Should post a code blocked response in the channel.
would like to have formatted code blocks using
```python
print()
```
or which ever language.

Actual behavior

Traceback (most recent call last):
  File "C:\Users\anomic\Desktop\code\bastard-guilded\venv\lib\site-packages\guilded\client.py", line 404, in _run_event
    await coro(*args, **kwargs)
  File "c:\Users\anomic\Desktop\code\bastard-guilded\main.py", line 92, in on_message
    await message.reply(response.choices[0].message.content)
  File "C:\Users\anomic\Desktop\code\bastard-guilded\venv\lib\site-packages\guilded\message.py", line 874, in reply
    return await self.channel.send(
  File "C:\Users\anomic\Desktop\code\bastard-guilded\venv\lib\site-packages\guilded\abc.py", line 180, in send
    data = await self._state.create_channel_message(
  File "C:\Users\anomic\Desktop\code\bastard-guilded\venv\lib\site-packages\guilded\http.py", line 527, in request
    raise GuildedServerError(response, data)
guilded.errors.GuildedServerError: 500 (InternalServerError): An unexpected error occured

Environment

  • OS: Win10
  • Python version (make sure you're using >=3.8): 3.9.13
  • Library version: guilded.py==1.7.0

pip broken?

I couldn't install the newest version of guilded.py.
pip index versions guilded.py returns

Available versions: 0.0.5, 0.0.4, 0.0.3, 0.0.2

installing version 1.0.0a0 worked, but it was too outdated.

bot example incorrect

you use

bot = commands.Bot(user_id='bot_user_id', command_prefix='!')

as the bot definition but use client.run instead bot.run in the bot example

Support all channel types, content, and operations

Channels in Guilded have a content type and content inside them. (And, usually, replies to that content). For the majority of content types this is represented in guilded.py as:

  • FooChannel (type: ChannelType.foo, subclasses abc.TeamChannel)
  • Foo
  • FooReply (subclasses abc.Reply)

Messageable channels do not follow this model.

ABCs

abc.TeamChannel

Inherited by all channel types except for DMChannel

Methods

  • edit
  • delete

abc.Messageable

Inherited by all channel types that can be messaged (i.e., their content is a ChatMessage) - ChatChannel, DMChannel, VoiceChannel, Thread, StreamingChannel

Methods

  • fetch_message
  • history
  • trigger_typing
  • send
  • pins
  • create_thread

Content - ChatMessage

This content can be replied to, but there is no ChatMessageReply model - replying simply creates another ChatMessage.

Gateway

  • Created
  • Modified
  • Deleted

Methods

  • add_reaction
  • remove_self_reaction
  • reply
  • edit (?)
  • pin
  • unpin
  • create_thread

abc.Reply

Inherited by all ___Reply models.

Gateway

  • Created
  • Modified
  • Deleted

Methods

  • edit
  • reply
  • add_reaction
  • remove_self_reaction
  • delete

Channels

AnnouncementChannel

Methods

  • get_announcement
  • fetch_announcement
  • fetch_announcements
  • fetch_pinned_announcements
  • create_announcement

Content - Announcement

This content can be replied to (AnnouncementReply).

Gateway

  • Created
  • Modified
  • Deleted

Methods

  • add_reaction
  • remove_self_reaction
  • reply
  • sticky
  • unsticky
  • edit
  • delete
  • get_reply
  • fetch_reply
  • fetch_replies

DocsChannel

Methods

  • get_doc
  • fetch_doc
  • fetch_docs
  • create_doc

Content - Doc

This content can be replied to (DocReply).

Gateway

  • Created
  • Modified
  • Deleted

Methods

  • add_reaction
  • remove_self_reaction
  • reply
  • delete
  • edit
  • move
  • get_reply
  • fetch_reply
  • fetch_replies

ForumChannel

Methods

  • get_topic
  • fetch_topic
  • fetch_topics
  • create_topic

Content - ForumTopic

This content can be replied to (ForumReply).

Gateway

  • Created
  • Modified
  • Deleted

Methods

  • add_reaction
  • remove_self_reaction
  • reply
  • edit
  • delete
  • move
  • lock
  • unlock
  • sticky
  • unsticky
  • get_reply
  • fetch_reply
  • fetch_replies

MediaChannel

Methods

  • get_media
  • fetch_media
  • fetch_medias
  • create_media

Content - Media

This content can be replied to (MediaReply).

Gateway

  • Created
  • Modified
  • Deleted

Methods

  • add_reaction
  • remove_self_reaction
  • reply
  • edit
  • delete
  • move
  • get_reply
  • fetch_reply
  • fetch_replies
  • read

ListChannel

Methods

  • get_item
  • fetch_item
  • fetch_items
  • create_item

Content - ListItem

This content cannot be replied to.

Gateway

  • Created
  • Modified
  • Deleted

Methods

  • delete
  • edit
  • move
  • fetch_note (ListItemNote)
  • fetch_parent (Higher-level ListItem)
  • create_item
  • complete
  • uncomplete

SchedulingChannel

Methods

  • get_availability
  • fetch_availability
  • fetch_availabilities
  • create_availability

Content - Availability

This content cannot be replied to.

Gateway

  • Created
  • Modified
  • Deleted

Methods

  • delete
  • edit

Wrong license message

Wrong license message

So in some files this include but not limited to: backoff.py, the license message is still belongs to Rapptz while LICENSE file is shay (shayypy). Is this on purpose?

Embeed the bot inside a fastapi server

Describe the bug
Start my fastapi server using uvicorn, send a "ping" in chat, bot answer with "pong", stop uvicorn server.
This is the stacktrace I get.

Ready
INFO:     Shutting down
INFO:     Waiting for application shutdown.
INFO:     Application shutdown complete.
INFO:     Finished server process [37840]
Traceback (most recent call last):
  File "C:\Users\xxx\AppData\Local\Programs\Python\Python310\lib\threading.py", line 1016, in _bootstrap_inner
    self.run()
  File "C:\Users\xxx\IdeaProjects\onr-bot-guilded\.tox\py310\lib\site-packages\guilded\gateway.py", line 1022, in run
    f = asyncio.run_coroutine_threadsafe(coro, loop=self.ws.loop)
  File "C:\Users\xxx\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py", line 885, in run_coroutine_threadsafe
    loop.call_soon_threadsafe(callback)
  File "C:\Users\xxx\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 795, in call_soon_threadsafe
    self._check_closed()
  File "C:\Users\xxx\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 515, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
python-BaseException
Exception in thread Thread-5:
Traceback (most recent call last):
  File "C:\Users\xxx\AppData\Local\Programs\Python\Python310\lib\threading.py", line 1016, in _bootstrap_inner
    self.run()
  File "C:\Users\xxx\IdeaProjects\onr-bot-guilded\.tox\py310\lib\site-packages\guilded\gateway.py", line 1022, in run
    f = asyncio.run_coroutine_threadsafe(coro, loop=self.ws.loop)
  File "C:\Users\xxx\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py", line 885, in run_coroutine_threadsafe
    loop.call_soon_threadsafe(callback)
  File "C:\Users\xxx\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 795, in call_soon_threadsafe
    self._check_closed()
  File "C:\Users\xxx\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 515, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
sys:1: RuntimeWarning: coroutine 'GuildedWebSocket.ping' was never awaited
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

Process finished with exit code 0

To Reproduce

import asyncio
import guilded
from fastapi import FastAPI

app = FastAPI()
client = guilded.Client()

@app.on_event("startup")
async def startup_event():
    logging.info('Starting server')
    asyncio.create_task(client.start(TOKEN))


@client.event
async def on_ready():
    print('Ready')

@client.event
async def on_message(message):
    if message.author == client.user:
        return
    if message.content == 'ping':
        await message.channel.send('pong!')

def main():
    import uvicorn
    uvicorn.run(app="app", host="0.0.0.0", port=8080, access_log=False, workers=1)


if __name__ == '__main__':
    main()

Expected behavior
I don't expect any stack trace in logs

Actual behavior
Stacktrace telling me RuntimeWarning: coroutine 'GuildedWebSocket.ping' was never awaited

Screenshots
If applicable, please attach screenshots to help explain/demonstrate the problem.

Environment

  • OS: Windows 10
  • Python version (make sure you're using >=3.8): 3.10
  • Library version: "guilded.py" = "^1.5.0"
    fastapi = "^0.85.2"
    uvicorn = "^0.19.0"

Command parser should merge user/channel mentions into a single argument

Currently, I have to explain to users that they need to surround a user or channel mention in quotation marks purely due to the fact that the library's command parser recognizes user/channel mentions with multiple words separated by spaces as individual arguments instead of a single argument. It would be much better if the command parser could recognize user/channel mentions toward users & channels in the bot's cache and merge them into one argument.

MemberConverter doesn't accept user mention as argument

Describe the bug
I can't target a user using a mention when attempting to convert, only with the raw name or user ID

To Reproduce
This fails
await commands.MemberConverter().convert(ctx, "@Henry")
This succeeds
await commands.MemberConverter().convert(ctx, "Henry")

Expected behavior
Return the user successfully

Actual behavior
Throws that it can't find the user

Embeds not Sending

Describe the bug
When attempting to send embeds, that have been made similar to in discord.py, you get a series of errors related to embeds.

To Reproduce
How to reproduce is in screenshot, but it is applicable to all embed modifications

Expected behavior
Bot would send an embed with gold colouring, and a single field, with Test as a title and Test as Content.

Actual behavior
Errors sent to console, copy and paste below
Ignoring exception in command test:
Traceback (most recent call last):
File "guilded/ext/commands/core.py", line 83, in wrapped
ret = await coro(*args, **kwargs)
File "PycharmProjects/Unified States/miscRW/credits.py", line 18, in test
await ctx.send(embed=embed)
File "guilded/abc.py", line 236, in send
data = await self._state.create_channel_message(self._channel_id,
File "guilded/http.py", line 1404, in request
return await perform()
File "guilded/http.py", line 1400, in perform
raise exception(response, data)
guilded.errors.BadRequest: 400 (BadRequestError): data must have required property 'content', data must have required property 'embeds', data must match a schema in anyOf

The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "guilded/ext/commands/bot.py", line 421, in invoke
await ctx.command.invoke(ctx)
File "guilded/ext/commands/core.py", line 619, in invoke
await injected(*ctx.args, **ctx.kwargs)
File "guilded/ext/commands/core.py", line 92, in wrapped
raise CommandInvokeError(exc) from exc
guilded.ext.commands.errors.CommandInvokeError: Command raised an exception: BadRequest: 400 (BadRequestError): data must have required property 'content', data must have required property 'embeds', data must match a schema in anyOf

Screenshots
image

Environment

  • OS: Arch Linux 2022.06.01 (Development PC) Ubuntu Server Bionic Beaver 18.04.6 LTS (Server)
  • Python version (make sure you're using >=3.8): Python 3.10 Both Environments.
  • Library version: 1.0.0a0 (0.0.5 lacks guilded.ext)

BotTeamMembershipCreated websocket event causes the bot to crash on server join

Describe the bug
When the bot tries to handle the BotTeamMembershipCreated WebSocket event, it causes a crash to occur.

To Reproduce
Have the bot join a server while it is running.

Expected behavior
The event should be handled appropriately and the bot should not crash.

Actual behavior
The bot crashes with the error "UnboundLocalError: local variable 'server' referenced before assignment"

Environment

  • OS: Linux
  • Python version (make sure you're using >=3.8): 3.10.7
  • Library version: 1.4.0

Cant send Embeds

Describe the bug
You cant send Embeds.

To Reproduce
Code:
@bot.command() async def EmbedTest(ctx): embed = guilded.Embed(Title="Test Title", desciption="Test Description", color=0x00ff00) embed.add_field(name="Field 1", value="Field 1 Value", inline=False) await ctx.send(embed=embed)

Screenshots
image

Code:
image

Environment

  • OS: Windows 10
  • Python version: 3.9
  • Library version: 1.0.0a0

Unable to send Media

Describe the bug
when attempting to send media to show Images or Diagrams, you are unable to send the media

To Reproduce
Attempt to send either files or attachments, via await ctx.send or await channel.send... etc

Expected behavior
Send message with a Media Attachment of a file.

Actual behavior
Message is sent without Media Attachment, and doesn't deliver an error message

Screenshots
image

Environment

  • OS: Arch Linux 2022.06.01 (Development PC) Ubuntu Server Bionic Beaver 18.04.6 LTS (Server)
  • Python version (make sure you're using >=3.8): Python 3.10 Both Environments.
  • Library version: 1.0.0a0 (0.0.5 lacks guilded.ext)

Implement rate limit buckets

Guilded's rate limits are pretty vague but it is possible to improve on the current system as a library. Notably, this issue suggests implementation of rate limit buckets in order to prevent extraneous requests from being made to the same bucket while the client is rate limited.

Partially related to this issue is the x-slowmode-cooldown header returned when the client fails to send a message to a channel with slowmode enabled. It contains the value in seconds of the slowmode setting for the channel. In this case, the bucket is the channelId.

Deleting a message not working

Describe the bug
await message.delete() returns a 403 error.

To Reproduce
Run the command

Expected behavior
Deleting the message

Actual behavior
It doesn't delete the message.

Environment

  • OS: Ubuntu 22.10
  • Python version (make sure you're using >=3.8): 3.10.7
  • Library version: 1.8.0

Better signatures for methods that accept structured content

Primer

Currently, most methods fitting this description have a signature like this:

async def method(*content, **kwargs):
    ...

... Which is not great for usability. The optimal signature is seen in methods like Messageable.send and ChatMessage.edit:

async def method(
    *pos_content: Optional[Union[str, Embed, File, Emoji, Member]],
    content: Optional[str] = MISSING,
    file: Optional[File] = MISSING,
    files: Optional[Sequence[File]] = MISSING,
    embed: Optional[Embed] = MISSING,
    embeds: Optional[Sequence[Embed]] = MISSING,
    ...
):
    ...

This provides a better typing experience & allows the user to pass content positionally (better for user accounts) or via a keyword-argument (discord.py-compatible & good for consistency).

To-do

  • ChatMessage.create_thread
  • Messageable.create_thread
  • User.send
  • Reply.reply
  • Reply.edit
  • Doc.edit
  • Doc.reply
  • DocsChannel.create_doc
  • ForumTopic.reply
  • ForumTopic.edit
  • ForumChannel.create_topic
  • Announcement.reply
  • Announcement.edit
  • AnnouncementChannel.create_announcement
  • Media.reply
  • ListItem.edit
    • This was a breaking change that changed how editing list item notes worked with this method. ListItemNote.edit should be used instead, but a new note_content can also be passed to ListItem.edit.
  • ListItem.create_item
  • ListItemNote.edit
  • ListChannel.create_item

Missing typing_extensions module

Describe the bug
Missing required module in the requirements.txt file

To Reproduce
Create a new bot project / virtualenv and install guilded.py with pip install guilded.py

Expected behavior
Basic bot like provided in the example should work

Actual behavior
Error ``ModuleNotFoundError: No module named 'typing_extensions'

Screenshots
image

Environment

  • OS: Windows 10
  • Python version (make sure you're using >=3.10):
  • Library version: 1.9.0

`message.author.send('some string')` not working

Describe the bug
Hi, I am trying to migrate a bot from discord to guilded. The bot generates a QR code and sends it to a member of the guild. The member types $qr in a guild channel and the bot then DM's the QR code to the member. However, the bot fails to DM the member.

To Reproduce
This is the on_message event listener I am using.

@client.event
# Listen for an incoming message
async def on_message(message):
    # If the author is the robot itself, then do nothing!
    if message.author == client.user:
        return
    # If the user writes $qr
    if message.content == "$qr":
        current_time = now.strftime("%H:%M:%S")
        print('\n')
        # This for loop check for all the user's DiscordID in the Database
        if str(message.author.id) in ScholarsDict:
            print("This user received his QR Code : " + message.author.name)
            print("Discord ID : " + str(message.author.id))
            print("Current time : ", current_time)
            # value with discordID
            botPlaceHolders = ScholarsDict[str(message.author.id)]
            # discordID's privateKey from the database
            accountPrivateKey = botPlaceHolders[2]
            # discordID's EthWalletAddress from the database
            accountAddress = botPlaceHolders[1]
            # Get a message from AxieInfinty
            rawMessage = getRawMessage()
            # Sign that message with accountPrivateKey
            signedMessage = getSignMessage(rawMessage, accountPrivateKey)
            # Get an accessToken by submitting the signature to AxieInfinty
            accessToken = submitSignature(signedMessage, rawMessage, accountAddress)
            # Create a QrCode with that accessToken
            qrCodePath = f"QRCode_{message.author.id}_{str(uuid.uuid4())[0:8]}.png"
            generate_qr(accessToken, qrCodePath)

            # Send the QrCode the the user who asked for
            await message.author.send(
                "Tips: If the qr code wont work, please try this steps. Delete the old qr on your device, and get a "
                "new one again, "
                "if that doesn't work then scan the qr code with another device."

                + message.author.name + "\nHere is your new QR Code to login : "
            )
            await message.author.send(file=guilded.File(qrCodePath))
            os.remove(qrCodePath)
            return ["bot_token"]
        else:
            print("This user didn't receive a QR Code because not added in SecretStorage: " + message.author.name)
            print("Guilded ID: " + str(message.author.id))
            print("Current time: ", current_time)
            return

Expected behavior
I expect the bot to DM the member the generated QR code

Actual behavior
I get this error instead

Ignoring exception in on_message:
Traceback (most recent call last):
  File "/home/kintama/Downloads/Cryptic-QR-Code-for-Guilded/venv/lib/python3.8/site-packages/guilded/client.py", line 353, in _run_event
    await coro(*args, **kwargs)
  File "Cryptic-Scholars-QR-Code-Bot.py", line 67, in on_message
    await message.author.send(
  File "/home/kintama/Downloads/Cryptic-QR-Code-for-Guilded/venv/lib/python3.8/site-packages/guilded/user.py", line 213, in general
    return await getattr(self._user, x)(*args, **kwargs)
  File "/home/kintama/Downloads/Cryptic-QR-Code-for-Guilded/venv/lib/python3.8/site-packages/guilded/abc.py", line 616, in send
    await self.create_dm()
  File "/home/kintama/Downloads/Cryptic-QR-Code-for-Guilded/venv/lib/python3.8/site-packages/guilded/abc.py", line 591, in create_dm
    data = await self._state.create_dm_channel([self.id])
AttributeError: 'HTTPClient' object has no attribute 'create_dm_channel'

Environment

  • OS: Ubuntu 20.04.4 LTS
  • Python version: 3.8.10
  • Library version: 17c0583

Embed timestamp error - not a valid ISO8601 datetime.

Describe the bug
If I want to add a timestamp (where I can only use datetime.datetime or Embed.Empty), guilded.py throws an error at me that it's not a valid ISO8601 datetime.

To Reproduce
Create an embed and add a timestamp:

embed = guilded.Embed(
            title="Title",
            description="Description"
        )
embed.timestamp = datetime.datetime.utcnow()
await ctx.send(embed=embed)

Expected behavior
I would expect that guilded.py would convert the timestamp to ISO8601 for me since I cannot use an ISO8601 str instead.

Actual behavior
I receive a TypeError and the message gets sent with a timestamp (not sure if it is correct, the embed shows the correct date at least)

Traceback (Note in the screenshot that I send an embed on an exception, so the traceback is doubled)
Traceback (most recent call last):
  File "/root/.local/lib/python3.9/site-packages/guilded/client.py", line 231, in _run_event
    await coro(*args, **kwargs)
  File "/Pumpkin/modules/base/errors/module.py", line 34, in on_error
    await bot_log.error(None, None, traceback=tb)
TypeError: error() missing 1 required positional argument: 'message'
Task exception was never retrieved
future: <Task finished name='Task-1' coro=<Client.start() done, defined at /root/.local/lib/python3.9/site-packages/guilded/client.py:277> exception=GuildedException(TypeError('2021-09-04T15:17:24.372247+00:00 is not a valid ISO8601 datetime.'))>
Traceback (most recent call last):
  File "/root/.local/lib/python3.9/site-packages/guilded/utils.py", line 75, in ISO8601
    return datetime.datetime.strptime(string, '%Y-%m-%dT%H:%M:%S.%fZ')
  File "/usr/local/lib/python3.9/_strptime.py", line 568, in _strptime_datetime
    tt, fraction, gmtoff_fraction = _strptime(data_string, format)
  File "/usr/local/lib/python3.9/_strptime.py", line 349, in _strptime
    raise ValueError("time data %r does not match format %r" %
ValueError: time data '2021-09-04T15:17:24.372247+00:00' does not match format '%Y-%m-%dT%H:%M:%S.%fZ'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/root/.local/lib/python3.9/site-packages/guilded/gateway.py", line 173, in received_event
    await event
  File "/root/.local/lib/python3.9/site-packages/guilded/gateway.py", line 239, in ChatMessageCreated
    message = self._state.create_message(channel=channel, data=data, author=author, team=team)
  File "/root/.local/lib/python3.9/site-packages/guilded/http.py", line 727, in create_message
    return ChatMessage(state=self, **data)
  File "/root/.local/lib/python3.9/site-packages/guilded/message.py", line 277, in __init__
    self.content = self._get_full_content(data)
  File "/root/.local/lib/python3.9/site-packages/guilded/message.py", line 424, in _get_full_content
    self.embeds.append(Embed.from_dict(msg_embed))
  File "/root/.local/lib/python3.9/site-packages/guilded/embed.py", line 191, in from_dict
    self._timestamp = utils.ISO8601(data['timestamp'])
  File "/root/.local/lib/python3.9/site-packages/guilded/utils.py", line 81, in ISO8601
    raise TypeError(f'{string} is not a valid ISO8601 datetime.')
TypeError: 2021-09-04T15:17:24.372247+00:00 is not a valid ISO8601 datetime.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/root/.local/lib/python3.9/site-packages/guilded/client.py", line 283, in start
    await self.connect()
  File "/root/.local/lib/python3.9/site-packages/guilded/client.py", line 372, in connect
    await asyncio.gather(
  File "/root/.local/lib/python3.9/site-packages/guilded/client.py", line 346, in listen_socks
    await ws.poll_event()
  File "/root/.local/lib/python3.9/site-packages/guilded/gateway.py", line 186, in poll_event
    await self.received_event(msg.data)
  File "/root/.local/lib/python3.9/site-packages/guilded/gateway.py", line 181, in received_event
    raise exc from e
guilded.errors.GuildedException: 2021-09-04T15:17:24.372247+00:00 is not a valid ISO8601 datetime.
Ignoring exception in on_command_error:
Traceback (most recent call last):
  File "/root/.local/lib/python3.9/site-packages/guilded/utils.py", line 75, in ISO8601
    return datetime.datetime.strptime(string, '%Y-%m-%dT%H:%M:%S.%fZ')
  File "/usr/local/lib/python3.9/_strptime.py", line 568, in _strptime_datetime
    tt, fraction, gmtoff_fraction = _strptime(data_string, format)
  File "/usr/local/lib/python3.9/_strptime.py", line 349, in _strptime
    raise ValueError("time data %r does not match format %r" %
ValueError: time data '2021-09-04T15:17:25.902639+00:00' does not match format '%Y-%m-%dT%H:%M:%S.%fZ'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/root/.local/lib/python3.9/site-packages/guilded/client.py", line 231, in _run_event
    await coro(*args, **kwargs)
  File "/Pumpkin/modules/base/errors/module.py", line 67, in on_command_error
    await ctx.send(embed=embed)
  File "/root/.local/lib/python3.9/site-packages/guilded/abc.py", line 188, in send
    return Message(state=self._state, channel=self, data=payload, author=author)
  File "/root/.local/lib/python3.9/site-packages/guilded/message.py", line 277, in __init__
    self.content = self._get_full_content(data)
  File "/root/.local/lib/python3.9/site-packages/guilded/message.py", line 424, in _get_full_content
    self.embeds.append(Embed.from_dict(msg_embed))
  File "/root/.local/lib/python3.9/site-packages/guilded/embed.py", line 191, in from_dict
    self._timestamp = utils.ISO8601(data['timestamp'])
  File "/root/.local/lib/python3.9/site-packages/guilded/utils.py", line 81, in ISO8601
    raise TypeError(f'{string} is not a valid ISO8601 datetime.')
TypeError: 2021-09-04T15:17:25.902639+00:00 is not a valid ISO8601 datetime.

Screenshot

Screenshot_20210904_171741

Environment

  • Host OS: Fedora 34
  • Docker image: python:3.9.5-slim
  • Python version: 3.9.5
  • Library version: 4a64e13

[Request] Hashable Types

Add __hash__ to the primary types used in the library, especially Server, User, and Channel
It should be pretty easy, and with some maintainer input I'd be happy to open a PR for this, perhaps builtin hash() of the ID?

Single backslash rstripped in input

Describe the bug
A single backslash can be placed at the end of an integer.
EG. -slots 200000\ results in the same thing as -slots 200000

To Reproduce

# using guilded.ext.commands.Bot
@bot.command(name='slots')
async def testcode(ctx, amount:int):
    await ctx.reply(str(amount), private=ctx.message.private)

Afterwards, just run -slots 200000\

Expected behavior
Raise an error: Converting to "int" failed for parameter "amount".

Actual behavior
Command runs as if the backslash isn't there.

Screenshots
image

Environment

  • OS: Windows 10 Microsoft Windows [Version 10.0.19044.2728] (testing) and Ubuntu 22.04.1 LTS x86-64 VM (hosting)
  • Python version (make sure you're using >=3.8): 3.10.6
  • Library version: 1.7.0 (March 4 release on PyPi.org)

Event rework

This issue outlines two enhancements to how events work in guilded.py. The first is the addition of more granular event control: restraining categories of events based on what your client needs. The second is a rework to how parameters are passed to event callbacks.

Granular events

discord.py implements a similar idea with its enable_debug_events parameter for Clients. This enhancement proposes the addition of this parameter as well as a use_discordpy_style_events parameter in order to "switch" event behavior to be more akin to discord.py. This would currently only detail event names (e.g., server_join vs. guild_join - only one or the other), but its effects are greater when considering the following change:

Event parameters

This change is considerably more impactful. If you are familiar with the JavaScript event system (with its Event and inheritors) then this may ring a bell for you. Instead of the following:

async def on_message(message):
    # message is a ChatMessage
    ...

you would handle events like so:

async def on_message(event):
    # event is a MessageEvent
    message = event.message
    # message is a ChatMessage

This change could also come with the decision to drop raw_ events in exchange for a more unified interface:

async def on_message_update(event):
    before = event.before  # could be None. Such a value indicates that the message was not cached.
    after = event.after  # always a ChatMessage

    # To perform cache-dependent logic, the user will essentially mirror the previous internal behavior:
    if before is None:
        return

This also flattens relatively convoluted events like raw_message_reaction_add and makes the whole event system function similarly. For example:

async def on_message_reaction_add(event):
    message = event.message  # could be None, but the user will always have event.message_id
    emote = event.emote  # Emote
    # The user will also have event.channel(_id), event.created_by(_id), and event.server(_id)

Guilded does not provide the rich data in its ChannelMessageReactionCreated/ChannelMessageReactionDeleted payloads that would make Reaction a better model to have here.

This would also enable events to include miscellaneous metadata, like that which is seen in TeamMemberRemoved:

async def on_member_remove(event):
    member = event.member  # could be None, but the user will always have event.server_id and event.user_id
    kicked = event.kicked
    banned = event.banned

This removes the need for extraneous member_leave and member_kick events which are not present in the bot API.

Conclusion

The latter part of this issue is currently available for use as an opt-in experiment, more details here. Feel free to discuss either of these in the #development channel (see the support header) or in this issue's comments.

Sending file raises guilded.errors.BadRequest: 400 error

When I try to send a .png file via Context.send(), it raises the following error. I have been trying to get it to work for quite a while but to no avail.

Here is the code:

# # Create a QrCode with that accessToken
        qrCodePath = f"QRCode_{ctx.author.name}_{str(uuid.uuid4())[0:8]}.png"
        generate_qr(accessToken, qrCodePath)
       
        # # Send the QrCode to the user
     
        await ctx.send(embed=guilded.Embed(title="Use the QR Code Above to Login",
                                           description="If the QR code doesn't work, please try these "
                                                       "steps: "
                                                       "\n\n**1.** Delete the old QR on your device, "
                                                       "and get a new one"
                                                       "\n**2.** If that doesn't work either then scan the "
                                                       "QR code with another device",
                                           colour=guilded.Colour.dark_purple(),
                                           timestamp=now,
                                           ),
                       file=guilded.File(fp=qrCodePath))

Here is the complete error log:

Ignoring exception in command qr:
Traceback (most recent call last):
  File "/home/kintama/Downloads/jerald/Cryptic-QR-Code-for-Guilded/venv/lib/python3.8/site-packages/guilded/ext/commands/core.py", line 83, in wrapped
    ret = await coro(*args, **kwargs)
  File "Cryptic-Scholars-QR-Code-Bot.py", line 74, in qr
    await ctx.send(embed=guilded.Embed(title="Use the QR Code Above to Login",
  File "/home/kintama/Downloads/jerald/Cryptic-QR-Code-for-Guilded/venv/lib/python3.8/site-packages/guilded/abc.py", line 254, in send
    data = await self._state.create_channel_message(
  File "/home/kintama/Downloads/jerald/Cryptic-QR-Code-for-Guilded/venv/lib/python3.8/site-packages/guilded/http.py", line 1614, in request
    return await perform()
  File "/home/kintama/Downloads/jerald/Cryptic-QR-Code-for-Guilded/venv/lib/python3.8/site-packages/guilded/http.py", line 1610, in perform
    raise exception(response, data)
guilded.errors.BadRequest: 400 (BadRequestError): Unsupported Content-Type header. Supported values: application/json

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/kintama/Downloads/jerald/Cryptic-QR-Code-for-Guilded/venv/lib/python3.8/site-packages/guilded/ext/commands/bot.py", line 421, in invoke
    await ctx.command.invoke(ctx)
  File "/home/kintama/Downloads/jerald/Cryptic-QR-Code-for-Guilded/venv/lib/python3.8/site-packages/guilded/ext/commands/core.py", line 619, in invoke
    await injected(*ctx.args, **ctx.kwargs)
  File "/home/kintama/Downloads/jerald/Cryptic-QR-Code-for-Guilded/venv/lib/python3.8/site-packages/guilded/ext/commands/core.py", line 92, in wrapped
    raise CommandInvokeError(exc) from exc
guilded.ext.commands.errors.CommandInvokeError: Command raised an exception: BadRequest: 400 (BadRequestError): Unsupported Content-Type header. Supported values: application/json

Sent inline mentions do not render in the client

Guilded.py uses the markdown-plain-text node type to avoid parsing markdown manually. Unfortunately, this causes Guilded to only render the text content of mentions (Members, Roles, TeamChannels) rather than their "rich" values.

This issue could be solved by switching to the paragraph type and having the library parse all markdown instead.

paragraph: image

markdown-plain-text: image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.