chat-exporter


Namechat-exporter JSON
Version 2.8.0 PyPI version JSON
download
home_pageNone
SummaryA simple Discord chat exporter for Python Discord bots.
upload_time2024-07-01 21:23:57
maintainerNone
docs_urlNone
authorNone
requires_python>=3.6
licenseGPL-3.0-only
keywords chat exporter discord chat exporter discord discordpy disnake pycord nextcord
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <div align="center">

[![Version][pypi-version]][pypi-url]
[![Language][language-dom]][github-url]
[![Forks][forks-shield]][forks-url]
[![Stargazers][stars-shield]][stars-url]
[![Issues][issues-shield]][issues-url]
[![GPL License][license-shield]][license-url]


  <h2>DiscordChatExporterPy</h2>

  <p>
    Export Discord chats with your discord.py (or fork) bots!
    <br />
    <a href="https://discord.mahto.id/">Join Discord</a>
    ·
    <a href="https://github.com/mahtoid/DiscordChatExporterPy/issues/new?assignees=&labels=bug&template=bug-report.yml">Report Bug</a>
    ·
    <a href="https://github.com/mahtoid/DiscordChatExporterPy/issues/new?assignees=&labels=enhancement&template=feature-request.yml">Request Feature</a>
  </p>
</div>

---
## Installation

To install the library to your virtual environment, for bot usage, run the command:
```sh 
pip install chat-exporter
```

To clone the repository locally, run the command:
```sh
git clone https://github.com/mahtoid/DiscordChatExporterPy
```

<p align="right">(<a href="#top">back to top</a>)</p>

---
## Usage

There are currently 3 methods (functions) to `chat-exporter` which you can use to export your chat.<br/>
_Expand the blocks below to learn the functions, arguments and usages._
<details><summary><b>Basic Usage</b></summary>

`.quick_export()` is the simplest way of using chat-exporter.

Using the _quick_export_ function will gather the history of the channel you give, build the transcript then post the file and embed directly to the channel - returning a message object gathered from the message it posted.

This is mostly seen as a demo function, as opposed to a command you should actually use. 

**Required Argument(s):**<br/>
`channel`: `discord.TextChannel` object, whether `ctx.channel` or any channel you gather.

**Optional Argument(s):**<br/>
`bot`: `commands.Bot` object to gather members who are no longer in your guild.

**Return Argument:**<br/>
`discord.Message`: The message _quick_export_ will send, containing the embed and exported chat file.

**Example:**
```python
import discord
import chat_exporter
from discord.ext import commands

intents = discord.Intents.default()
intents.members = True
intents.message_content = True

bot = commands.Bot(command_prefix="!", intents=intents)

...

@bot.command()
async def save(ctx: commands.Context):
    await chat_exporter.quick_export(ctx.channel)

...
```

</details>

<details><summary><b>Customisable Usage</b></summary>

`.export()` is the most efficient and flexible method to export a chat using chat-exporter.

Using the _export_ function will generate a transcript using the channel you pass in, along with using any of the custom kwargs passed in to set limits, timezone, 24h formats and more (listed below).

This would be the main function to use within chat-exporter.

**Required Argument(s):**<br/>
`channel`: `discord.TextChannel` object, whether `ctx.channel` or any channel you gather.

**Optional Argument(s):**<br/>
`limit`: Integer value to set the limit (amount of messages) the chat exporter gathers when grabbing the history (default=unlimited).<br/>
`tz_info`: String value of a [TZ Database name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) to set a custom timezone for the exported messages (default=UTC).<br/>
`guild`: `discord.Guild` object which can be passed in to solve bugs for certain forks.<br/>
`military_time`: Boolean value to set a 24h format for times within your exported chat (default=False | 12h format).<br/>
`fancy_times`: Boolean value which toggles the 'fancy times' (Today|Yesterday|Day).<br/>
`before`: `datetime.datetime` object which allows to gather messages from before a certain date.<br/>
`after`: `datetime.datetime` object which allows to gather messages from after a certain date.<br/>
`bot`: `commands.Bot` object to gather members who are no longer in your guild.<br/>
`attachment_handler`: `chat_exporter.AttachmentHandler` object to export assets to in order to make them available after the `channel` got deleted.<br/>

**Return Argument:**<br/>
`transcript`: The HTML build-up for you to construct the HTML File with Discord.

**Example:**
```python
import io

...

@bot.command()
async def save(ctx: commands.Context, limit: int = 100, tz_info: str = "UTC", military_time: bool = True):
    transcript = await chat_exporter.export(
        ctx.channel,
        limit=limit,
        tz_info=tz_info,
        military_time=military_time,
        bot=bot,
    )

    if transcript is None:
        return

    transcript_file = discord.File(
        io.BytesIO(transcript.encode()),
        filename=f"transcript-{ctx.channel.name}.html",
    )

    await ctx.send(file=transcript_file)
```
</details>
<details><summary><b>Raw Usage</b></summary>

`.raw_export()` is for the crazy people who like to do their own thing when using chat-exporter.

Using the _raw_export_ function will generate a transcript using the list of messages you pass in, along with using any of the custom kwargs passed in to set limits, timezone, 24h formats and more (listed below).

This would be for people who want to filter what content to export.

**Required Argument(s):**<br/>
`channel`: `discord.TextChannel` object, whether `ctx.channel` or any channel you gather (this is just for padding the header).<br/>
`messages`: A list of Message objects which you wish to export to an HTML file.

**Optional Argument(s):**<br/>
`tz_info`: String value of a [TZ Database name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) to set a custom timezone for the exported messages (default=UTC)<br/>
`military_time`: Boolean value to set a 24h format for times within your exported chat (default=False | 12h format)<br/>
`fancy_times`: Boolean value which toggles the 'fancy times' (Today|Yesterday|Day)<br/>
`bot`: `commands.Bot` object to gather members who are no longer in your guild.
`attachment_handler`: `chat_exporter.AttachmentHandler` object to export assets to in order to make them available after the `channel` got deleted.<br/>

**Return Argument:**<br/>
`transcript`: The HTML build-up for you to construct the HTML File with Discord.

**Example:**
```python
import io

...

@bot.command()
async def purge(ctx: commands.Context, tz_info: str, military_time: bool):
    deleted_messages = await ctx.channel.purge()

    transcript = await chat_exporter.raw_export(
        ctx.channel,
        messages=deleted_messages,
        tz_info=tz_info,
        military_time=military_time,
        bot=bot,
    )

    if transcript is None:
        return

    transcript_file = discord.File(
        io.BytesIO(transcript.encode()),
        filename=f"transcript-{ctx.channel.name}.html",
    )

    await ctx.send(file=transcript_file)
```
</details>


<p align="right">(<a href="#top">back to top</a>)</p>

---
## Attachment Handler

Due to Discords newly introduced restrictions on to their CDN, we have introduced an Attachment Handler. This handler
will assist you with circumventing the 'broken' and 'dead-assets' which arise when former attachments hosted by Discord
reach their expiration date.

The `AttachmentHandler` serves as a template for you to implement your own asset handler. Below are two basic examples on
how to use the `AttachmentHandler`. One using the example of storing files on a local webserver, with the other being
an example of storing them on Discord *(the latter merely just being an example, this will still obviously run in to
the expiration issue)*.

If you do not specify an attachment handler, chat-exporter will continue to use the (proxy) URLs for the assets.

<details><summary><b>Concept</b></summary>

The concept of implementing such an AttachmentHandler is very easy. In the following a short general procedure is 
described to write your own AttachmentHandler fitting your storage solution. Here we will assume, that we store the 
attachments in a cloud storage.

1. Subclassing
Start by subclassing `chat_exporter.AttachmentHandler` and implement the `__init__` method if needed. This should look 
something like this:

```python
from chat_exporter import AttachmentHandler
from cloud_wrapper import CloudClient


class MyAttachmentHandler(AttachmentHandler):
    def __init__(self, *args, **kwargs):
        # Your initialization code here
        # in your case we just create the cloud client
        self.cloud_client = CloudClient()

```

2. Overwrite process_asset
The `process_asset` method is the method that is called for each asset in the chat. Here we have to implement the 
upload logic and the generation of the asset url from the uploaded asset.
    
```python
import io
import aiohttp
from chat_exporter import AttachmentHandler
from cloud_wrapper import CloudClient
from discord import Attachment


class MyAttachmentHandler(AttachmentHandler):
    def __init__(self, *args, **kwargs):
        # Your initialization code here
        # in your case we just create the cloud client
        self.cloud_client = CloudClient()

    async def process_asset(self, attachment: Attachment):
        # Your upload logic here, in our example we just upload the asset to the cloud
        
        # first we need to authorize the client
        await self.cloud_client.authorize()
        
        # then we fetch the content of the attachment
        async with aiohttp.ClientSession() as session:
            async with session.get(attachment.url) as res:
                if res.status != 200:
                    res.raise_for_status()
                data = io.BytesIO(await res.read())
        data.seek(0)
        
        # and upload it to the cloud, back we get some sort of identifier for the uploaded file
        asset_id = await self.cloud_client.upload(data)
        
        # now we can generate the asset url from the identifier
        asset_url = await self.cloud_client.get_share_url(asset_id, shared_with="everyone")
        
        # and set the proxy url attribute of the attachment to the generated url
        attachment.proxy_url = asset_url
        return attachment

```

Note
1. The `process_asset` method should return the attachment object with the proxy_url attribute set to the generated url.
2. The `process_asset` method should be an async method, as it is likely that you have to do some async operations 
   like fetching the content of the attachment or uploading it to the cloud.
3. You are free to add other methods in your class, and call them from `process_asset` if you need to do some 
   operations before or after the upload of the asset. But the `process_asset` method is the only method that is 
called from chat-exporter.

</details>

**Examples:**

<ol>
<details><summary>AttachmentToLocalFileHostHandler</summary>

Assuming you have a file server running, which serves the content of the folder `/usr/share/assets/` 
under `https://example.com/assets/`, you can easily use the `AttachmentToLocalFileHostHandler` like this:
```python
import io
import discord
from discord.ext import commands
import chat_exporter
from chat_exporter import AttachmentToLocalFileHostHandler

...

# Establish the file handler
file_handler = AttachmentToLocalFileHostHandler(
    base_path="/usr/share/assets",
    url_base="https://example.com/assets/",
)

@bot.command()
async def save(ctx: commands.Context):
    transcript = await chat_exporter.export(
        ctx.channel,
        attachment_handler=file_handler,
    )

    if transcript is None:
        return

    transcript_file = discord.File(
        io.BytesIO(transcript.encode()),
        filename=f"transcript-{ctx.channel.name}.html",
    )

    await ctx.send(file=transcript_file)

```
</details>

<details><summary>AttachmentToDiscordChannel</summary>

Assuming you want to store your attachments in a discord channel, you can use the `AttachmentToDiscordChannel`. 
Please note that discord recent changes regarding content links will result in the attachments links being broken 
after 24 hours. While this is therefor not a recommended way to store your attachments, it should give you a good 
idea how to perform asynchronous storing of the attachments.

```python
import io
import discord
from discord.ext import commands
import chat_exporter
from chat_exporter import AttachmentToDiscordChannel

...

# Establish the file handler
channel_handler = AttachmentToDiscordChannel(
    channel=bot.get_channel(CHANNEL_ID),
)

@bot.command()
async def save(ctx: commands.Context):
    transcript = await chat_exporter.export(
        ctx.channel,
        attachment_handler=channel_handler,
    )

    if transcript is None:
        return

    transcript_file = discord.File(
        io.BytesIO(transcript.encode()),
        filename=f"transcript-{ctx.channel.name}.html",
    )

    await ctx.send(file=transcript_file)

```
</details>
</ol>
<p align="right">(<a href="#top">back to top</a>)</p>

---
## Screenshots

<details><summary><b>General</b></summary>
<ol>
    <details><summary>Discord</summary>
    <img src="https://raw.githubusercontent.com/mahtoid/DiscordChatExporterPy/master/.screenshots/channel_output.png">
    </details>
    <details><summary>Chat-Exporter</summary>
    <img src="https://raw.githubusercontent.com/mahtoid/DiscordChatExporterPy/master/.screenshots/html_output.png">
    </details>
</ol>
</details>
<p align="right">(<a href="#top">back to top</a>)</p>


---
## Additional Functions


<details><summary><b>Link Function</b></summary>
Downloading exported chats can build up a bunch of unwanted files on your PC which can get annoying, additionally - not everyone wants to download content from Discord.

Due to these pain, and many requests - I have built a fancy PHP script which will show the transcript file within a browser.<br/>
<ol>
<details><summary>quick_link</summary>
Similar in design to `.quick_export()` this is a bit of a demo function to produce a link and to give you an embed.

**Required Argument(s):**<br/>
`channel`: `discord.TextChannel` object, whether `ctx.channel` or any channel you gather.<br/>
`message`: The Discord message containing the transcript file

**Return Argument:**<br/>
`discord.Message`: The message _quick_link_ will send, containing the embed.

**Example:**
```python
import chat_exporter

...

@bot.command()
async def save(ctx: commands.Context):
    message = await chat_exporter.quick_export(ctx.channel)
    await chat_exporter.quick_link(ctx.channel, message)
```
</details>

<details><summary>link</summary>
A simple function to return the link you will need to view the transcript online.

**Required Argument(s):**<br/>
`message`: The Discord message containing the transcript file

**Return Argument:**<br/>
`link`: The link to view the transcript file online

**Example:**
```python
import io

import chat_exporter

...

@bot.command()
async def save(ctx: commands.Context):
    transcript = await chat_exporter.export(ctx.channel)
    
    if transcript is None:
        return

    transcript_file = discord.File(
        io.BytesIO(transcript.encode()),
        filename=f"transcript-{ctx.channel.name}.html",
    )

    message = await ctx.send(file=transcript_file)
    link = await chat_exporter.link(message)

    await ctx.send("Click this link to view the transcript online: " + link)
```
</details>
</ol>

_Please note that the PHP script does NOT store any information.<br/>
It simply makes a request to the given URL and echos (prints) the content for you to be able to view it._

</details>



---
## Attributions

*This project borrows CSS and HTML code from [Tyrrrz's C# DiscordChatExporter](https://github.com/Tyrrrz/DiscordChatExporter/) repository.*

<p align="right">(<a href="#top">back to top</a>)</p>

<!-- LINK DUMP -->
[pypi-version]: https://img.shields.io/pypi/v/chat-exporter?style=for-the-badge
[pypi-url]: https://pypi.org/project/chat-exporter/
[language-dom]: https://img.shields.io/github/languages/top/mahtoid/discordchatexporterpy?style=for-the-badge
[forks-shield]: https://img.shields.io/github/forks/mahtoid/DiscordChatExporterPy?style=for-the-badge
[forks-url]: https://github.com/mahtoid/DiscordChatExporterPy/
[stars-shield]: https://img.shields.io/github/stars/mahtoid/DiscordChatExporterPy?style=for-the-badge
[stars-url]: https://github.com/mahtoid/DiscordChatExporterPy/stargazers
[issues-shield]: https://img.shields.io/github/issues/mahtoid/DiscordChatExporterPy?style=for-the-badge
[issues-url]: https://github.com/mahtoid/DiscordChatExporterPy/issues
[license-shield]: https://img.shields.io/github/license/mahtoid/DiscordChatExporterPy?style=for-the-badge
[license-url]: https://github.com/mahtoid/DiscordChatExporterPy/blob/master/LICENSE
[github-url]: https://github.com/mahtoid/DiscordChatExporterPy/

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "chat-exporter",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.6",
    "maintainer_email": null,
    "keywords": "chat exporter, discord chat exporter, discord, discordpy, disnake, pycord, nextcord",
    "author": null,
    "author_email": "mahtoid <info@mahto.id>",
    "download_url": "https://files.pythonhosted.org/packages/bb/5b/4f41162ce3041dd70041c70ffd45917dbbe1f3b19013f6157c484d6653ed/chat_exporter-2.8.0.tar.gz",
    "platform": null,
    "description": "<div align=\"center\">\r\n\r\n[![Version][pypi-version]][pypi-url]\r\n[![Language][language-dom]][github-url]\r\n[![Forks][forks-shield]][forks-url]\r\n[![Stargazers][stars-shield]][stars-url]\r\n[![Issues][issues-shield]][issues-url]\r\n[![GPL License][license-shield]][license-url]\r\n\r\n\r\n  <h2>DiscordChatExporterPy</h2>\r\n\r\n  <p>\r\n    Export Discord chats with your discord.py (or fork) bots!\r\n    <br />\r\n    <a href=\"https://discord.mahto.id/\">Join Discord</a>\r\n    \u00b7\r\n    <a href=\"https://github.com/mahtoid/DiscordChatExporterPy/issues/new?assignees=&labels=bug&template=bug-report.yml\">Report Bug</a>\r\n    \u00b7\r\n    <a href=\"https://github.com/mahtoid/DiscordChatExporterPy/issues/new?assignees=&labels=enhancement&template=feature-request.yml\">Request Feature</a>\r\n  </p>\r\n</div>\r\n\r\n---\r\n## Installation\r\n\r\nTo install the library to your virtual environment, for bot usage, run the command:\r\n```sh \r\npip install chat-exporter\r\n```\r\n\r\nTo clone the repository locally, run the command:\r\n```sh\r\ngit clone https://github.com/mahtoid/DiscordChatExporterPy\r\n```\r\n\r\n<p align=\"right\">(<a href=\"#top\">back to top</a>)</p>\r\n\r\n---\r\n## Usage\r\n\r\nThere are currently 3 methods (functions) to `chat-exporter` which you can use to export your chat.<br/>\r\n_Expand the blocks below to learn the functions, arguments and usages._\r\n<details><summary><b>Basic Usage</b></summary>\r\n\r\n`.quick_export()` is the simplest way of using chat-exporter.\r\n\r\nUsing the _quick_export_ function will gather the history of the channel you give, build the transcript then post the file and embed directly to the channel - returning a message object gathered from the message it posted.\r\n\r\nThis is mostly seen as a demo function, as opposed to a command you should actually use. \r\n\r\n**Required Argument(s):**<br/>\r\n`channel`: `discord.TextChannel` object, whether `ctx.channel` or any channel you gather.\r\n\r\n**Optional Argument(s):**<br/>\r\n`bot`: `commands.Bot` object to gather members who are no longer in your guild.\r\n\r\n**Return Argument:**<br/>\r\n`discord.Message`: The message _quick_export_ will send, containing the embed and exported chat file.\r\n\r\n**Example:**\r\n```python\r\nimport discord\r\nimport chat_exporter\r\nfrom discord.ext import commands\r\n\r\nintents = discord.Intents.default()\r\nintents.members = True\r\nintents.message_content = True\r\n\r\nbot = commands.Bot(command_prefix=\"!\", intents=intents)\r\n\r\n...\r\n\r\n@bot.command()\r\nasync def save(ctx: commands.Context):\r\n    await chat_exporter.quick_export(ctx.channel)\r\n\r\n...\r\n```\r\n\r\n</details>\r\n\r\n<details><summary><b>Customisable Usage</b></summary>\r\n\r\n`.export()` is the most efficient and flexible method to export a chat using chat-exporter.\r\n\r\nUsing the _export_ function will generate a transcript using the channel you pass in, along with using any of the custom kwargs passed in to set limits, timezone, 24h formats and more (listed below).\r\n\r\nThis would be the main function to use within chat-exporter.\r\n\r\n**Required Argument(s):**<br/>\r\n`channel`: `discord.TextChannel` object, whether `ctx.channel` or any channel you gather.\r\n\r\n**Optional Argument(s):**<br/>\r\n`limit`: Integer value to set the limit (amount of messages) the chat exporter gathers when grabbing the history (default=unlimited).<br/>\r\n`tz_info`: String value of a [TZ Database name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) to set a custom timezone for the exported messages (default=UTC).<br/>\r\n`guild`: `discord.Guild` object which can be passed in to solve bugs for certain forks.<br/>\r\n`military_time`: Boolean value to set a 24h format for times within your exported chat (default=False | 12h format).<br/>\r\n`fancy_times`: Boolean value which toggles the 'fancy times' (Today|Yesterday|Day).<br/>\r\n`before`: `datetime.datetime` object which allows to gather messages from before a certain date.<br/>\r\n`after`: `datetime.datetime` object which allows to gather messages from after a certain date.<br/>\r\n`bot`: `commands.Bot` object to gather members who are no longer in your guild.<br/>\r\n`attachment_handler`: `chat_exporter.AttachmentHandler` object to export assets to in order to make them available after the `channel` got deleted.<br/>\r\n\r\n**Return Argument:**<br/>\r\n`transcript`: The HTML build-up for you to construct the HTML File with Discord.\r\n\r\n**Example:**\r\n```python\r\nimport io\r\n\r\n...\r\n\r\n@bot.command()\r\nasync def save(ctx: commands.Context, limit: int = 100, tz_info: str = \"UTC\", military_time: bool = True):\r\n    transcript = await chat_exporter.export(\r\n        ctx.channel,\r\n        limit=limit,\r\n        tz_info=tz_info,\r\n        military_time=military_time,\r\n        bot=bot,\r\n    )\r\n\r\n    if transcript is None:\r\n        return\r\n\r\n    transcript_file = discord.File(\r\n        io.BytesIO(transcript.encode()),\r\n        filename=f\"transcript-{ctx.channel.name}.html\",\r\n    )\r\n\r\n    await ctx.send(file=transcript_file)\r\n```\r\n</details>\r\n<details><summary><b>Raw Usage</b></summary>\r\n\r\n`.raw_export()` is for the crazy people who like to do their own thing when using chat-exporter.\r\n\r\nUsing the _raw_export_ function will generate a transcript using the list of messages you pass in, along with using any of the custom kwargs passed in to set limits, timezone, 24h formats and more (listed below).\r\n\r\nThis would be for people who want to filter what content to export.\r\n\r\n**Required Argument(s):**<br/>\r\n`channel`: `discord.TextChannel` object, whether `ctx.channel` or any channel you gather (this is just for padding the header).<br/>\r\n`messages`: A list of Message objects which you wish to export to an HTML file.\r\n\r\n**Optional Argument(s):**<br/>\r\n`tz_info`: String value of a [TZ Database name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) to set a custom timezone for the exported messages (default=UTC)<br/>\r\n`military_time`: Boolean value to set a 24h format for times within your exported chat (default=False | 12h format)<br/>\r\n`fancy_times`: Boolean value which toggles the 'fancy times' (Today|Yesterday|Day)<br/>\r\n`bot`: `commands.Bot` object to gather members who are no longer in your guild.\r\n`attachment_handler`: `chat_exporter.AttachmentHandler` object to export assets to in order to make them available after the `channel` got deleted.<br/>\r\n\r\n**Return Argument:**<br/>\r\n`transcript`: The HTML build-up for you to construct the HTML File with Discord.\r\n\r\n**Example:**\r\n```python\r\nimport io\r\n\r\n...\r\n\r\n@bot.command()\r\nasync def purge(ctx: commands.Context, tz_info: str, military_time: bool):\r\n    deleted_messages = await ctx.channel.purge()\r\n\r\n    transcript = await chat_exporter.raw_export(\r\n        ctx.channel,\r\n        messages=deleted_messages,\r\n        tz_info=tz_info,\r\n        military_time=military_time,\r\n        bot=bot,\r\n    )\r\n\r\n    if transcript is None:\r\n        return\r\n\r\n    transcript_file = discord.File(\r\n        io.BytesIO(transcript.encode()),\r\n        filename=f\"transcript-{ctx.channel.name}.html\",\r\n    )\r\n\r\n    await ctx.send(file=transcript_file)\r\n```\r\n</details>\r\n\r\n\r\n<p align=\"right\">(<a href=\"#top\">back to top</a>)</p>\r\n\r\n---\r\n## Attachment Handler\r\n\r\nDue to Discords newly introduced restrictions on to their CDN, we have introduced an Attachment Handler. This handler\r\nwill assist you with circumventing the 'broken' and 'dead-assets' which arise when former attachments hosted by Discord\r\nreach their expiration date.\r\n\r\nThe `AttachmentHandler` serves as a template for you to implement your own asset handler. Below are two basic examples on\r\nhow to use the `AttachmentHandler`. One using the example of storing files on a local webserver, with the other being\r\nan example of storing them on Discord *(the latter merely just being an example, this will still obviously run in to\r\nthe expiration issue)*.\r\n\r\nIf you do not specify an attachment handler, chat-exporter will continue to use the (proxy) URLs for the assets.\r\n\r\n<details><summary><b>Concept</b></summary>\r\n\r\nThe concept of implementing such an AttachmentHandler is very easy. In the following a short general procedure is \r\ndescribed to write your own AttachmentHandler fitting your storage solution. Here we will assume, that we store the \r\nattachments in a cloud storage.\r\n\r\n1. Subclassing\r\nStart by subclassing `chat_exporter.AttachmentHandler` and implement the `__init__` method if needed. This should look \r\nsomething like this:\r\n\r\n```python\r\nfrom chat_exporter import AttachmentHandler\r\nfrom cloud_wrapper import CloudClient\r\n\r\n\r\nclass MyAttachmentHandler(AttachmentHandler):\r\n    def __init__(self, *args, **kwargs):\r\n        # Your initialization code here\r\n        # in your case we just create the cloud client\r\n        self.cloud_client = CloudClient()\r\n\r\n```\r\n\r\n2. Overwrite process_asset\r\nThe `process_asset` method is the method that is called for each asset in the chat. Here we have to implement the \r\nupload logic and the generation of the asset url from the uploaded asset.\r\n    \r\n```python\r\nimport io\r\nimport aiohttp\r\nfrom chat_exporter import AttachmentHandler\r\nfrom cloud_wrapper import CloudClient\r\nfrom discord import Attachment\r\n\r\n\r\nclass MyAttachmentHandler(AttachmentHandler):\r\n    def __init__(self, *args, **kwargs):\r\n        # Your initialization code here\r\n        # in your case we just create the cloud client\r\n        self.cloud_client = CloudClient()\r\n\r\n    async def process_asset(self, attachment: Attachment):\r\n        # Your upload logic here, in our example we just upload the asset to the cloud\r\n        \r\n        # first we need to authorize the client\r\n        await self.cloud_client.authorize()\r\n        \r\n        # then we fetch the content of the attachment\r\n        async with aiohttp.ClientSession() as session:\r\n            async with session.get(attachment.url) as res:\r\n                if res.status != 200:\r\n                    res.raise_for_status()\r\n                data = io.BytesIO(await res.read())\r\n        data.seek(0)\r\n        \r\n        # and upload it to the cloud, back we get some sort of identifier for the uploaded file\r\n        asset_id = await self.cloud_client.upload(data)\r\n        \r\n        # now we can generate the asset url from the identifier\r\n        asset_url = await self.cloud_client.get_share_url(asset_id, shared_with=\"everyone\")\r\n        \r\n        # and set the proxy url attribute of the attachment to the generated url\r\n        attachment.proxy_url = asset_url\r\n        return attachment\r\n\r\n```\r\n\r\nNote\r\n1. The `process_asset` method should return the attachment object with the proxy_url attribute set to the generated url.\r\n2. The `process_asset` method should be an async method, as it is likely that you have to do some async operations \r\n   like fetching the content of the attachment or uploading it to the cloud.\r\n3. You are free to add other methods in your class, and call them from `process_asset` if you need to do some \r\n   operations before or after the upload of the asset. But the `process_asset` method is the only method that is \r\ncalled from chat-exporter.\r\n\r\n</details>\r\n\r\n**Examples:**\r\n\r\n<ol>\r\n<details><summary>AttachmentToLocalFileHostHandler</summary>\r\n\r\nAssuming you have a file server running, which serves the content of the folder `/usr/share/assets/` \r\nunder `https://example.com/assets/`, you can easily use the `AttachmentToLocalFileHostHandler` like this:\r\n```python\r\nimport io\r\nimport discord\r\nfrom discord.ext import commands\r\nimport chat_exporter\r\nfrom chat_exporter import AttachmentToLocalFileHostHandler\r\n\r\n...\r\n\r\n# Establish the file handler\r\nfile_handler = AttachmentToLocalFileHostHandler(\r\n    base_path=\"/usr/share/assets\",\r\n    url_base=\"https://example.com/assets/\",\r\n)\r\n\r\n@bot.command()\r\nasync def save(ctx: commands.Context):\r\n    transcript = await chat_exporter.export(\r\n        ctx.channel,\r\n        attachment_handler=file_handler,\r\n    )\r\n\r\n    if transcript is None:\r\n        return\r\n\r\n    transcript_file = discord.File(\r\n        io.BytesIO(transcript.encode()),\r\n        filename=f\"transcript-{ctx.channel.name}.html\",\r\n    )\r\n\r\n    await ctx.send(file=transcript_file)\r\n\r\n```\r\n</details>\r\n\r\n<details><summary>AttachmentToDiscordChannel</summary>\r\n\r\nAssuming you want to store your attachments in a discord channel, you can use the `AttachmentToDiscordChannel`. \r\nPlease note that discord recent changes regarding content links will result in the attachments links being broken \r\nafter 24 hours. While this is therefor not a recommended way to store your attachments, it should give you a good \r\nidea how to perform asynchronous storing of the attachments.\r\n\r\n```python\r\nimport io\r\nimport discord\r\nfrom discord.ext import commands\r\nimport chat_exporter\r\nfrom chat_exporter import AttachmentToDiscordChannel\r\n\r\n...\r\n\r\n# Establish the file handler\r\nchannel_handler = AttachmentToDiscordChannel(\r\n    channel=bot.get_channel(CHANNEL_ID),\r\n)\r\n\r\n@bot.command()\r\nasync def save(ctx: commands.Context):\r\n    transcript = await chat_exporter.export(\r\n        ctx.channel,\r\n        attachment_handler=channel_handler,\r\n    )\r\n\r\n    if transcript is None:\r\n        return\r\n\r\n    transcript_file = discord.File(\r\n        io.BytesIO(transcript.encode()),\r\n        filename=f\"transcript-{ctx.channel.name}.html\",\r\n    )\r\n\r\n    await ctx.send(file=transcript_file)\r\n\r\n```\r\n</details>\r\n</ol>\r\n<p align=\"right\">(<a href=\"#top\">back to top</a>)</p>\r\n\r\n---\r\n## Screenshots\r\n\r\n<details><summary><b>General</b></summary>\r\n<ol>\r\n    <details><summary>Discord</summary>\r\n    <img src=\"https://raw.githubusercontent.com/mahtoid/DiscordChatExporterPy/master/.screenshots/channel_output.png\">\r\n    </details>\r\n    <details><summary>Chat-Exporter</summary>\r\n    <img src=\"https://raw.githubusercontent.com/mahtoid/DiscordChatExporterPy/master/.screenshots/html_output.png\">\r\n    </details>\r\n</ol>\r\n</details>\r\n<p align=\"right\">(<a href=\"#top\">back to top</a>)</p>\r\n\r\n\r\n---\r\n## Additional Functions\r\n\r\n\r\n<details><summary><b>Link Function</b></summary>\r\nDownloading exported chats can build up a bunch of unwanted files on your PC which can get annoying, additionally - not everyone wants to download content from Discord.\r\n\r\nDue to these pain, and many requests - I have built a fancy PHP script which will show the transcript file within a browser.<br/>\r\n<ol>\r\n<details><summary>quick_link</summary>\r\nSimilar in design to `.quick_export()` this is a bit of a demo function to produce a link and to give you an embed.\r\n\r\n**Required Argument(s):**<br/>\r\n`channel`: `discord.TextChannel` object, whether `ctx.channel` or any channel you gather.<br/>\r\n`message`: The Discord message containing the transcript file\r\n\r\n**Return Argument:**<br/>\r\n`discord.Message`: The message _quick_link_ will send, containing the embed.\r\n\r\n**Example:**\r\n```python\r\nimport chat_exporter\r\n\r\n...\r\n\r\n@bot.command()\r\nasync def save(ctx: commands.Context):\r\n    message = await chat_exporter.quick_export(ctx.channel)\r\n    await chat_exporter.quick_link(ctx.channel, message)\r\n```\r\n</details>\r\n\r\n<details><summary>link</summary>\r\nA simple function to return the link you will need to view the transcript online.\r\n\r\n**Required Argument(s):**<br/>\r\n`message`: The Discord message containing the transcript file\r\n\r\n**Return Argument:**<br/>\r\n`link`: The link to view the transcript file online\r\n\r\n**Example:**\r\n```python\r\nimport io\r\n\r\nimport chat_exporter\r\n\r\n...\r\n\r\n@bot.command()\r\nasync def save(ctx: commands.Context):\r\n    transcript = await chat_exporter.export(ctx.channel)\r\n    \r\n    if transcript is None:\r\n        return\r\n\r\n    transcript_file = discord.File(\r\n        io.BytesIO(transcript.encode()),\r\n        filename=f\"transcript-{ctx.channel.name}.html\",\r\n    )\r\n\r\n    message = await ctx.send(file=transcript_file)\r\n    link = await chat_exporter.link(message)\r\n\r\n    await ctx.send(\"Click this link to view the transcript online: \" + link)\r\n```\r\n</details>\r\n</ol>\r\n\r\n_Please note that the PHP script does NOT store any information.<br/>\r\nIt simply makes a request to the given URL and echos (prints) the content for you to be able to view it._\r\n\r\n</details>\r\n\r\n\r\n\r\n---\r\n## Attributions\r\n\r\n*This project borrows CSS and HTML code from [Tyrrrz's C# DiscordChatExporter](https://github.com/Tyrrrz/DiscordChatExporter/) repository.*\r\n\r\n<p align=\"right\">(<a href=\"#top\">back to top</a>)</p>\r\n\r\n<!-- LINK DUMP -->\r\n[pypi-version]: https://img.shields.io/pypi/v/chat-exporter?style=for-the-badge\r\n[pypi-url]: https://pypi.org/project/chat-exporter/\r\n[language-dom]: https://img.shields.io/github/languages/top/mahtoid/discordchatexporterpy?style=for-the-badge\r\n[forks-shield]: https://img.shields.io/github/forks/mahtoid/DiscordChatExporterPy?style=for-the-badge\r\n[forks-url]: https://github.com/mahtoid/DiscordChatExporterPy/\r\n[stars-shield]: https://img.shields.io/github/stars/mahtoid/DiscordChatExporterPy?style=for-the-badge\r\n[stars-url]: https://github.com/mahtoid/DiscordChatExporterPy/stargazers\r\n[issues-shield]: https://img.shields.io/github/issues/mahtoid/DiscordChatExporterPy?style=for-the-badge\r\n[issues-url]: https://github.com/mahtoid/DiscordChatExporterPy/issues\r\n[license-shield]: https://img.shields.io/github/license/mahtoid/DiscordChatExporterPy?style=for-the-badge\r\n[license-url]: https://github.com/mahtoid/DiscordChatExporterPy/blob/master/LICENSE\r\n[github-url]: https://github.com/mahtoid/DiscordChatExporterPy/\r\n",
    "bugtrack_url": null,
    "license": "GPL-3.0-only",
    "summary": "A simple Discord chat exporter for Python Discord bots.",
    "version": "2.8.0",
    "project_urls": {
        "Discord": "https://discord.mahto.id/",
        "Donate": "https://ko-fi.com/mahtoid",
        "Homepage": "https://github.com/mahtoid/DiscordChatExporterPy"
    },
    "split_keywords": [
        "chat exporter",
        " discord chat exporter",
        " discord",
        " discordpy",
        " disnake",
        " pycord",
        " nextcord"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4bfa6dec01b4fcbfdcebcc24df58aca0a7e61e44d498ecca9c13debcdf702de5",
                "md5": "c6440620210dedccec7c43c8d45aa576",
                "sha256": "0170da9399638903e654c9bc531f3e8ca6b6329d852f48c0153ebc6863fa3476"
            },
            "downloads": -1,
            "filename": "chat_exporter-2.8.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "c6440620210dedccec7c43c8d45aa576",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.6",
            "size": 65862,
            "upload_time": "2024-07-01T21:23:53",
            "upload_time_iso_8601": "2024-07-01T21:23:53.304151Z",
            "url": "https://files.pythonhosted.org/packages/4b/fa/6dec01b4fcbfdcebcc24df58aca0a7e61e44d498ecca9c13debcdf702de5/chat_exporter-2.8.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "bb5b4f41162ce3041dd70041c70ffd45917dbbe1f3b19013f6157c484d6653ed",
                "md5": "a78cafe170f11a54490aa6b058bd51d3",
                "sha256": "8db130bf082c62b5dc07a5387d27d6bc070ae0d3e8da29529ec6be925a46c1d6"
            },
            "downloads": -1,
            "filename": "chat_exporter-2.8.0.tar.gz",
            "has_sig": false,
            "md5_digest": "a78cafe170f11a54490aa6b058bd51d3",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.6",
            "size": 55653,
            "upload_time": "2024-07-01T21:23:57",
            "upload_time_iso_8601": "2024-07-01T21:23:57.302319Z",
            "url": "https://files.pythonhosted.org/packages/bb/5b/4f41162ce3041dd70041c70ffd45917dbbe1f3b19013f6157c484d6653ed/chat_exporter-2.8.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-07-01 21:23:57",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "mahtoid",
    "github_project": "DiscordChatExporterPy",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "chat-exporter"
}
        
Elapsed time: 0.32760s