Skip to content

Latest commit

 

History

History
251 lines (212 loc) · 11.1 KB

File metadata and controls

251 lines (212 loc) · 11.1 KB

DO NOT CONTRIBUTE

You'll save your mental health, and I will save time trying to understand your code. Open an issue, and I will get to it.
The code is total mess that only I can efficiently navigate through, and if someone else adds any-quality code to it, it will only get harder for me.
Any PR will be rejected, sorry.

LLM generated code is strongly prohibited

If PR is suspected to have more than 5% of AI generated code, it will be closed ASAP, and the features will be "really" implemented by someone else, without any association with that PR. No exceptions.
So, before even thinking about putting it through a LLM, open an issue and save yourself the trouble, because others can do it for free and better.

Contributing rules

  • Don't use inheritance. It makes code even more unreadable.
  • Don't use dataclasses, it's too late now, use nested lists and dicts.
  • Don't write tests, this cant be tested.
  • NO typing!
  • Don't refactor, format (other than the existing ruff config), clean or unnecessarily optimize. I like the code the way it is.
  • Don't use requests, it uses 3MB more RAM than http.client.
  • Use os.path instead pathlib, its making things weird.
  • NO asyncio, this is pure threading project.
  • If you know how to do it, and it's not really hard, do it yourself, don't import large libraries for that.

Running from source

  1. setup uv environment if not already: uv sync --all-groups
  2. Run main.py: uv run main.py

Architecture

Endcord is developed in "Modular composition and component-based architecture using singleton services with a central orchestrator". Explanation:

  • Modular component-based - program is split into independent modules (classes in separate files), each responsible for a specific thing.
  • Composition - main app class contains and uses instances of other classes instead of inheriting from them.
  • Singleton services - for each class only one instance is used.
  • Central orchestrator - Main app class is controller that: creates all instances, wires them together, does core logic.

Useful debugging things

Debug points in the code

  • debug_events - save all received events from gateway
  • debug_guilds_tree - print all tree data in jsons
  • 255_curses_bug - this part of the code should be changed after ncurses bug is fixed. If there is no note, just remove the code
  • fix_member_list_selection - properly select member list when figured out how to use id in GUILD_MEMBER_LIST_UPDATE.

Network tab filter

Filter for network tab in dev tools:
-.js -css -woff -svg -webp -png -ico -webm -science -txt -mp3

Monitor IPC on linux socket

sudo mv /run/user/1000/discord-ipc-0 /run/user/1000/discord-ipc-0.original
sudo socat -t100 -x -v UNIX-LISTEN:/run/user/1000/discord-ipc-0,mode=777,reuseaddr,fork UNIX-CONNECT:/run/user/1000/discord-ipc-0.original

Log discord events to console

Open discord web or install discord-development
Or regular discord: in .config/discord/config.json put: "DANGEROUS_ENABLE_DEVTOOLS_ONLY_ENABLE_IF_YOU_KNOW_WHAT_YOURE_DOING": true
Open dev tools: Ctrl+Shift+I
Type: allow pasting
Paste code from here
Go to discord settings, in developer options, logging tab, enable "Logging Gateway Events to Console"
In dev tools console select "Verbose" level (chrome and desktop client only)

Full API documentation

https://github.com/discord-userdoccers/discord-userdoccers

App command permissions chart

https://discord.com/developers/docs/change-log#upcoming-application-command-permission-changes

Layout

Standard:

┌────────────────────────────────────────────────┐
│┌─TITLE─┐┌─────────────── TITLE ─────┐┌────────┐│
││       ││                           ││        ││
││       ││                           ││ MEMBER ││
││       ││            CHAT           ││  LIST  ││
││       ││                           ││        ││
││       ││                           ││        ││
││ TREE  │└───────────────────────────┘└────────┘│
││       │┌────────────── EXTRA2 ───────────────┐│
││       ││             EXTRA BODY              ││
││       │┌────────────── EXTRA1 ───────────────┐│
││       │├────────────── STATUS ───────────────┤│
││       ││[PROMPT]>                            ││
│└───────┘└─────────────────────────────────────┘│
└────────────────────────────────────────────────┘

Compact:

┌────────────────────────────────────────────────┐
│W TITLE W│WWWWWWWWWWWWWWW TITLE WWWWWWWWWWWWWWWW│
│         │                             │        │
│         │                             │        │
|         │                             │ MEMBER │
│         │            CHAT             │  LIST  │
│         │                             │        │
│         │                             │        │
│  TREE   │                             │        │
│         │MMMMMMMMMMMMMMM EXTRA2 MMMMMMMMMMMMMMM│
│         │              EXTRA BODY              │
│         │UUUUUUUUUUUUUUU EXTRA1 UUUUUUUUUUUUUUU│
│         │WWWWWWWWWWWWWWW STATUS WWWWWWWWWWWWWWW│
│         │[PROMPT]>                             │
└────────────────────────────────────────────────┘

Tree layout and formatting

> FOLDER
> GUILD
|--> CATEGORY
|  |-- CHANNEL
|  |--> CHANNEL
|  |  |-< THREAD
|  |  \-< THREAD
|  |  end_channel 1300
|  \--> CHANNEL
|  end_category 1200
|--> CATEGORY
|  end_category 1200
end_guild 1100
end_folder 1000

Support for zstd stream compression:

inflator = zstandard.ZstdDecompressor().decompressobj()
def zstd_decompress(data):
    """Decompress zstd stream"""
    buffer = bytearray(data)
    try:
        return inflator.decompress(buffer)
    except zstandard.ZstdError as e:
        logger.error(f"zstd error: {e}")
        return None

Use &compress=zstd-stream in gateway url.

Decode X-Super-Properties

import base64
import json
encoded = "STRING_HERE"
decoded = json.loads(base64.b64decode(encoded).decode("utf-8"))
print(json.dumps(decoded, indent=2))

Spacebar differences

In the code, all spacebar fixes can be found as # spacebar_fix - [note].
[note] contains some information on what is changed.

  • /api/v9/users/@me/mentions: /global_name - missing

  • /api/v9/users/{my_id}/channels: /recipients/global_name - missing

  • /api/v9/users/{user_id}/profile:
    /user/avatar_decoration_data - missing
    user/flags - missing
    /user/global_name - missing
    /user/primary_guild - missing

  • MESSAGE_CREATE, MESSAGE_UPDATE, /api/v9/channels/{channel_id}/messages, /api/v9/guilds/{guild_id}/messages/search and /api/v9/channels/{channel_id}/pins: /author/global_name - missing
    /referenced_message - should be null only if its pointing to delteted message, otherwise remove this field
    /referenced_message/author/global_name - missing
    /reactions/emoji/id - missing
    /poll - should be removed if its null
    /interaction - should be removed if its null
    /mentions/username - missing
    /embeds/type - missing
    /edited_timestamp - missing for some messages

  • READY:
    /merged_members/id - should be /merged_members/user_id
    /read_state/entries/mention_count - should be 0, not null
    /private_channels/recipient_ids - missing, have recipients list instead /users/global_name - missing
    /user_guild_settings/entries/channel_overrides/collapsed - missing

  • TYPING_START: /member/user/global_name - missing

  • GUILD_MEMBER_LIST_UPDATE: /items/member/user/global_name - missing
    /item/member/user/global_name - missing

  • MESSAGE_REACTION_ADD: /member/user - missing
    /emoji/id - missing when its standard emoji

  • MESSAGE_REACTION_REMOVE: /emoji/id - missing when its standard emoji

  • GUILD_MEMBERS_CHUNK: /members/user/global_name - missing

  • PRESENCE_UPDATE: /status - missing

  • Misc: Spacebar is still using old user_settings instead new protobuf settings.
    Gateway returns error code 4000 if event "update presence" (opcode 3) is sent.

Build steps for package maintainers

  1. setup dependencies
  • Any python virtual environment manager can be used that can read dependencies from pyproject.toml, here uv is used by default.
  • Only python versions 3.12-3.13 can currently build binaries.
  • uv sync --all-groups - full endcord
    Will install all dependencies from pyproject.toml under dependencies, build, and media.
  • uv sync --group build - endcord-lite
    Will install all dependencies from pyproject.toml under dependencies and build.
  1. Check if numpy without openblas is already installed, if returns 1 them it must download and build numpy
  • Numpy build can be skipped if its impossible, but binary will be few MB larger.
  • If building with pyinstaller skip numpy build.
  • This command will print 1 if openblas is found in numpy, and then it has to be built locally.
uv run python -c "import numpy; print(int(numpy.__config__.show_config('dicts')['Build Dependencies']['blas'].get('found', False)))"
  1. Download and build numpy
  • Clang is optional everywhere, but it's recommended as it provides better binary.
export CC=clang
export CXX=clang++
uv pip install pip
.venv/bin/python -m pip uninstall --yes numpy
.venv/bin/python  -m pip install --no-cache-dir --no-binary=:all: numpy --config-settings=setup-args=-Dblas=None --config-settings=setup-args=-Dlapack=None
uv pip uninstall pip
  1. Build cython extensions
  • Skip this step only if final binary gives error Dynamic module does not define module export function.
export CC=clang
export CXX=clang++
uv run python setup.py build_ext --inplace
  1. Run patches and cleanups
  • run patch_soundcard() from build.py - required!
  • run clean_emoji() from build.py - will remove many non-standard emoji.
  • run clean_qrcode() from build.py - will reduce binary by ~100KB.
  1. Get and run build command
  • Build commands can be obtained by running python build.py --print-cmd, adding other arguments will change the printed command.
  • Recommended: python build.py --print-cmd --nuitka --clang.