Init
This commit is contained in:
parent
f934b7ff99
commit
5c9564c703
44
L10n/de.strings
Normal file
44
L10n/de.strings
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/* Language name */
|
||||||
|
"!Language" = "Deutsch";
|
||||||
|
|
||||||
|
/* Idle message. [load.py] */
|
||||||
|
"Connecting CMDR Interface" = "Verbindet mit KMDT-Schnittstelle";
|
||||||
|
|
||||||
|
/* Details of system. [load.py] */
|
||||||
|
"In system {system}" = "Im System {system}";
|
||||||
|
|
||||||
|
/* If docked. [load.py] */
|
||||||
|
"Docked at {station}" = "Angedockt an {station}";
|
||||||
|
|
||||||
|
/* While jumping. [load.py] */
|
||||||
|
"Jumping" = "Springt";
|
||||||
|
|
||||||
|
/* If Hyperspace jumping. [load.py] */
|
||||||
|
"Jumping to system {system}" = "Springt zum System {system}";
|
||||||
|
|
||||||
|
/* If Supercruise jumping. [load.py] */
|
||||||
|
"Preparing for supercruise" = "Macht sich bereit für Supercruise";
|
||||||
|
|
||||||
|
/* When supercruising. [load.py] */
|
||||||
|
"Supercruising" = "Im Supercruise";
|
||||||
|
|
||||||
|
/* When in normal space. [load.py] */
|
||||||
|
"Flying in normal space" = "Fliegt im normalen Raum";
|
||||||
|
|
||||||
|
/* When in normal space and near a station. [load.py] */
|
||||||
|
"Flying near {station}" = "Fliegt nahe {station}";
|
||||||
|
|
||||||
|
/* When approaching a body. [load.py] */
|
||||||
|
"Approaching {body}" = "Nähert sich {body}";
|
||||||
|
|
||||||
|
/* When landed on a body. [load.py] */
|
||||||
|
"Landed on {body}" = "Gelandet auf {body}";
|
||||||
|
|
||||||
|
/* After taking off from a body. [load.py] */
|
||||||
|
"Flying around {body}" = "Fliegt um {body}";
|
||||||
|
|
||||||
|
/* When in SRV. [load.py] */
|
||||||
|
"In SRV on {body}" = "Im SRV auf {body}";
|
||||||
|
|
||||||
|
/* When in SRV and ship has taken off. [load.py] */
|
||||||
|
"In SRV on {body}, ship in orbit" = "Im SRV auf {body}, Schiff im Orbit";
|
44
L10n/en.template
Normal file
44
L10n/en.template
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/* Language name */
|
||||||
|
"!Language" = "English";
|
||||||
|
|
||||||
|
/* Idle message. [load.py] */
|
||||||
|
"Connecting CMDR Interface" = "Connecting CMDR Interface";
|
||||||
|
|
||||||
|
/* Details of system. [load.py] */
|
||||||
|
"In system {system}" = "In system {system}";
|
||||||
|
|
||||||
|
/* If docked. [load.py] */
|
||||||
|
"Docked at {station}" = "Docked at {station}";
|
||||||
|
|
||||||
|
/* While jumping. [load.py] */
|
||||||
|
"Jumping" = "Jumping";
|
||||||
|
|
||||||
|
/* If Hyperspace jumping. [load.py] */
|
||||||
|
"Jumping to system {system}" = "Jumping to system {system}";
|
||||||
|
|
||||||
|
/* If Supercruise jumping. [load.py] */
|
||||||
|
"Preparing for supercruise" = "Preparing for supercruise";
|
||||||
|
|
||||||
|
/* When supercruising. [load.py] */
|
||||||
|
"Supercruising" = "Supercruising";
|
||||||
|
|
||||||
|
/* When in normal space. [load.py] */
|
||||||
|
"Flying in normal space" = "Flying in normal space";
|
||||||
|
|
||||||
|
/* When in normal space and near a station. [load.py] */
|
||||||
|
"Flying near {station}" = "Flying near {station}";
|
||||||
|
|
||||||
|
/* When approaching a body. [load.py] */
|
||||||
|
"Approaching {body}" = "Approaching {body}";
|
||||||
|
|
||||||
|
/* When landed on a body. [load.py] */
|
||||||
|
"Landed on {body}" = "Landed on {body}";
|
||||||
|
|
||||||
|
/* After taking off from a body. [load.py] */
|
||||||
|
"Flying around {body}" = "Flying around {body}";
|
||||||
|
|
||||||
|
/* When in SRV. [load.py] */
|
||||||
|
"In SRV on {body}" = "In SRV on {body}";
|
||||||
|
|
||||||
|
/* When in SRV and ship has taken off. [load.py] */
|
||||||
|
"In SRV on {body}, ship in orbit" = "In SRV on {body}, ship in orbit";
|
44
L10n/fr.strings
Normal file
44
L10n/fr.strings
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/* Language name */
|
||||||
|
"!Language" = "Français";
|
||||||
|
|
||||||
|
/* Idle message. [load.py] */
|
||||||
|
"Connecting CMDR Interface" = "Interfaçage au Vaisseau";
|
||||||
|
|
||||||
|
/* Details of system. [load.py] */
|
||||||
|
"In system {system}" = "Dans le système {system}";
|
||||||
|
|
||||||
|
/* If docked. [load.py] */
|
||||||
|
"Docked at {station}" = "Docké à {station}";
|
||||||
|
|
||||||
|
/* While jumping. [load.py] */
|
||||||
|
"Jumping" = "Saut";
|
||||||
|
|
||||||
|
/* If Hyperspace jumping. [load.py] */
|
||||||
|
"Jumping to system {system}" = "Saut vers {system}";
|
||||||
|
|
||||||
|
/* If Supercruise jumping. [load.py] */
|
||||||
|
"Preparing for supercruise" = "Préparation d\'un saut en supercruise";
|
||||||
|
|
||||||
|
/* When supercruising. [load.py] */
|
||||||
|
"Supercruising" = "Supercruise";
|
||||||
|
|
||||||
|
/* When in normal space. [load.py] */
|
||||||
|
"Flying in normal space" = "En vol dans l\'espace";
|
||||||
|
|
||||||
|
/* When in normal space and near a station. [load.py] */
|
||||||
|
"Flying near {station}" = "En vol près de {station}";
|
||||||
|
|
||||||
|
/* When approaching a body. [load.py] */
|
||||||
|
"Approaching {body}" = "En approche de {body}";
|
||||||
|
|
||||||
|
/* When landed on a body. [load.py] */
|
||||||
|
"Landed on {body}" = "Posé sur {body}";
|
||||||
|
|
||||||
|
/* After taking off from a body. [load.py] */
|
||||||
|
"Flying around {body}" = "Vol autour de {body}";
|
||||||
|
|
||||||
|
/* When in SRV. [load.py] */
|
||||||
|
"In SRV on {body}" = "En SRV sur {body}";
|
||||||
|
|
||||||
|
/* When in SRV and ship has taken off. [load.py] */
|
||||||
|
"In SRV on {body}, ship in orbit" = "En SRV sur {body}, vaisseau en orbite";
|
44
L10n/pt-BR.strings
Normal file
44
L10n/pt-BR.strings
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/* Language name */
|
||||||
|
"!Language" = "Português (Brasil)";
|
||||||
|
|
||||||
|
/* Idle message. [load.py] */
|
||||||
|
"Connecting CMDR Interface" = "Conectando Interface do CMDT";
|
||||||
|
|
||||||
|
/* Details of system. [load.py] */
|
||||||
|
"In system {system}" = "No sistema {system}";
|
||||||
|
|
||||||
|
/* If docked. [load.py] */
|
||||||
|
"Docked at {station}" = "Docado em {station}";
|
||||||
|
|
||||||
|
/* While jumping. [load.py] */
|
||||||
|
"Jumping" = "Saltando";
|
||||||
|
|
||||||
|
/* If Hyperspace jumping. [load.py] */
|
||||||
|
"Jumping to system {system}" = "Saltando para systema {system}";
|
||||||
|
|
||||||
|
/* If Supercruise jumping. [load.py] */
|
||||||
|
"Preparing for supercruise" = "Preparando para supercruseiro";
|
||||||
|
|
||||||
|
/* When supercruising. [load.py] */
|
||||||
|
"Supercruising" = "Supercrusando";
|
||||||
|
|
||||||
|
/* When in normal space. [load.py] */
|
||||||
|
"Flying in normal space" = "Voando em espaço normal";
|
||||||
|
|
||||||
|
/* When in normal space and near a station. [load.py] */
|
||||||
|
"Flying near {station}" = "Voando perto de {station}";
|
||||||
|
|
||||||
|
/* When approaching a body. [load.py] */
|
||||||
|
"Approaching {body}" = "Aproximando-se de {body}";
|
||||||
|
|
||||||
|
/* When landed on a body. [load.py] */
|
||||||
|
"Landed on {body}" = "Pousado em {body}";
|
||||||
|
|
||||||
|
/* After taking off from a body. [load.py] */
|
||||||
|
"Flying around {body}" = "Voando ao redor de {body}";
|
||||||
|
|
||||||
|
/* When in SRV. [load.py] */
|
||||||
|
"In SRV on {body}" = "No SRV em {body}";
|
||||||
|
|
||||||
|
/* When in SRV and ship has taken off. [load.py] */
|
||||||
|
"In SRV on {body}, in orbit" = "No SRV em {body}, nave em órbita";
|
44
L10n/ru.strings
Normal file
44
L10n/ru.strings
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/* Language name */
|
||||||
|
"!Language" = "Русский";
|
||||||
|
|
||||||
|
/* Idle message. [load.py] */
|
||||||
|
"Connecting CMDR Interface" = "Подключение интерфейса КМДР";
|
||||||
|
|
||||||
|
/* Details of system. [load.py] */
|
||||||
|
"In system {system}" = "В системе {system}";
|
||||||
|
|
||||||
|
/* If docked. [load.py] */
|
||||||
|
"Docked at {station}" = "Пристыкован к {station}";
|
||||||
|
|
||||||
|
/* While jumping. [load.py] */
|
||||||
|
"Jumping" = "Гиперпрыжок";
|
||||||
|
|
||||||
|
/* If Hyperspace jumping. [load.py] */
|
||||||
|
"Jumping to system {system}" = "Гиперпрыжок в систему {system}";
|
||||||
|
|
||||||
|
/* If Supercruise jumping. [load.py] */
|
||||||
|
"Preparing for supercruise" = "Подготовка к переходу в супереркруиз";
|
||||||
|
|
||||||
|
/* When supercruising. [load.py] */
|
||||||
|
"Supercruising" = "Полет в суперкруизе";
|
||||||
|
|
||||||
|
/* When in normal space. [load.py] */
|
||||||
|
"Flying in normal space" = "Полет в обычном космосе";
|
||||||
|
|
||||||
|
/* When in normal space and near a station. [load.py] */
|
||||||
|
"Flying near {station}" = "Полет вблизи {station}";
|
||||||
|
|
||||||
|
/* When approaching a body. [load.py] */
|
||||||
|
"Approaching {body}" = "Приближение к {body}";
|
||||||
|
|
||||||
|
/* When landed on a body. [load.py] */
|
||||||
|
"Landed on {body}" = "Посадка на {body}";
|
||||||
|
|
||||||
|
/* After taking off from a body. [load.py] */
|
||||||
|
"Flying around {body}" = "Полет возле {body}";
|
||||||
|
|
||||||
|
/* When in SRV. [load.py] */
|
||||||
|
"In SRV on {body}" = "В ТРП на поверхности {body}";
|
||||||
|
|
||||||
|
/* When in SRV and ship has taken off. [load.py] */
|
||||||
|
"In SRV on {body}, ship in orbit" = "В ТРП на поверхности {body}, судно на орбите";
|
49
README.md
49
README.md
|
@ -1,3 +1,46 @@
|
||||||
# EliteRPC
|
# EDMC-Discord-Presence
|
||||||
|
|
||||||
Discord Rich Presence for Elite Dangerous
|
A plugin for [Elite Dangerous Market Connector](https://github.com/Marginal/EDMarketConnector) that enables [Discord Rich Presence](https://discordapp.com/rich-presence) for [Elite Dangerous](https://www.elitedangerous.com/)
|
||||||
|
|
||||||
|
Show your current location to your friends on Discord from your user profile.
|
||||||
|
|
||||||
|
![Presence Screenshot](EDMC_Discord_Presence_1.png?raw=true)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. [Install EDMC according to instructions](https://github.com/Marginal/EDMarketConnector)
|
||||||
|
2. Download the latest version of the plugin from [here](https://github.com/SayakMukhopadhyay/EDMC-Discord-Presence/releases). Make sure to download the release `.zip` and not the source code bundle.
|
||||||
|
3. Open Elite Dangerous Market Connector and go to File -> Settings. Then browse to the plugins tab:
|
||||||
|
|
||||||
|
![Plugin Installation](EDMC_Discord_Presence_2.png?raw=true)
|
||||||
|
|
||||||
|
4. Click "Open" to open the plugins directory.
|
||||||
|
5. Open the Zip file we have downloaded and drag the folder from within into the plugins directory
|
||||||
|
6. Restart EDMC for the plugin to take effect.
|
||||||
|
|
||||||
|
To check if the plugin is loaded correctly, go File -> Settings. Then browse to the plugins tab. `DiscordPresence` must be listed under `Enabled Plugins`
|
||||||
|
|
||||||
|
![Plugin Installation Check](EDMC_Discord_Presence_3.png?raw=true)
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
You can set the plugin to not show your game data. Go to File -> Settings. Under the `DiscordPresence` tab, check `Disable Presence`
|
||||||
|
|
||||||
|
![Plugin Disable](EDMC_Discord_Presence_4.png?raw=true)
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
If you find a bug, please create an issue in the issue tracker in Github, properly detailing the bug and reproduction steps.
|
||||||
|
|
||||||
|
If you are willing to contribute to the project, please work on a fork and create a pull request.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
For the CMDRs by a CMDR. Created by [CMDR Garud](https://forums.frontier.co.uk/member.php/136073-Garud) for an awesome gaming community.
|
||||||
|
A big thanks to [Jonathan Harris (Marginal)](https://github.com/Marginal) for creating the Python boilerplate code for the Discord Rich Presence SDK. Without his input, the plugin would not have been done. Special mention for the awesome group I am in, [Knights of Karma](http://knightsofkarma.com/), for their continuous support.
|
||||||
|
|
||||||
|
Translate to french, migrate from python2 to python3 by [Poneyy](https://github.com/Poneyy)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Developed under [Apache License 2.0](https://choosealicense.com/licenses/apache-2.0/).
|
||||||
|
|
BIN
__pycache__/load.cpython-312.pyc
Normal file
BIN
__pycache__/load.cpython-312.pyc
Normal file
Binary file not shown.
BIN
lib/discord_game_sdk.dll
Normal file
BIN
lib/discord_game_sdk.dll
Normal file
Binary file not shown.
BIN
lib/discord_game_sdk.dylib
Normal file
BIN
lib/discord_game_sdk.dylib
Normal file
Binary file not shown.
BIN
lib/discord_game_sdk.so
Normal file
BIN
lib/discord_game_sdk.so
Normal file
Binary file not shown.
356
load.py
Normal file
356
load.py
Normal file
|
@ -0,0 +1,356 @@
|
||||||
|
#
|
||||||
|
# KodeBlox Copyright 2019 Sayak Mukhopadhyay
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http: //www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
import functools
|
||||||
|
import logging
|
||||||
|
import threading
|
||||||
|
import tkinter as tk
|
||||||
|
from os.path import dirname, join
|
||||||
|
|
||||||
|
import semantic_version
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
import l10n
|
||||||
|
import myNotebook as nb
|
||||||
|
from config import config, appname, appversion
|
||||||
|
from py_discord_sdk import discordsdk as dsdk
|
||||||
|
|
||||||
|
plugin_name = "DiscordPresenceNG"
|
||||||
|
|
||||||
|
logger = logging.getLogger(f'{appname}.{plugin_name}')
|
||||||
|
|
||||||
|
_ = functools.partial(l10n.Translations.translate, context=__file__)
|
||||||
|
|
||||||
|
CLIENT_ID = 1013117837310693438
|
||||||
|
VERSION = '1.0'
|
||||||
|
|
||||||
|
# Add global var for Planet name (landing + around)
|
||||||
|
planet = '<Hidden>'
|
||||||
|
landingPad = '2'
|
||||||
|
|
||||||
|
this = sys.modules[__name__] # For holding module globals
|
||||||
|
|
||||||
|
def ship(ship_type):
|
||||||
|
ship_name = ''
|
||||||
|
match ship_type:
|
||||||
|
case "sidewinder":
|
||||||
|
ship_name = "Sidewinder"
|
||||||
|
case "eagle":
|
||||||
|
ship_name = "Eagle"
|
||||||
|
case "hauler":
|
||||||
|
ship_name = "Hauler"
|
||||||
|
case "adder":
|
||||||
|
ship_name = "Adder"
|
||||||
|
case "adder_taxi":
|
||||||
|
ship_name = "Apex Shuttle"
|
||||||
|
case "empire_eagle":
|
||||||
|
ship_name = "Imperial Eagle"
|
||||||
|
case "viper":
|
||||||
|
ship_name = "Viper Mk III"
|
||||||
|
case "cobramkiii":
|
||||||
|
ship_name = "Cobra Mk III"
|
||||||
|
case "viper_mkiv":
|
||||||
|
ship_name = "Viper Mk IV"
|
||||||
|
case "diamondback":
|
||||||
|
ship_name = "Diamondback Scout"
|
||||||
|
case "cobramkiv":
|
||||||
|
ship_name = "Cobra Mk IV"
|
||||||
|
case "type6":
|
||||||
|
ship_name = "Type-6 Transporter"
|
||||||
|
case "dolphin":
|
||||||
|
ship_name = "Dolphin"
|
||||||
|
case "diamondbackxl":
|
||||||
|
ship_name = "Diamondback Explorer"
|
||||||
|
case "empire_courier":
|
||||||
|
ship_name = "Imperial Courier"
|
||||||
|
case "independant_trader":
|
||||||
|
ship_name = "Keelback"
|
||||||
|
case "asp_scout":
|
||||||
|
ship_name = "Asp Scout"
|
||||||
|
case "vulture":
|
||||||
|
ship_name = "Vulture"
|
||||||
|
case "asp":
|
||||||
|
ship_name = "Asp Explorer"
|
||||||
|
case "federation_dropship":
|
||||||
|
ship_name = "Federal Dropship"
|
||||||
|
case "type7":
|
||||||
|
ship_name = "Type-7 Transporter"
|
||||||
|
case "typex":
|
||||||
|
ship_name = "Alliance Chieftain"
|
||||||
|
case "federation_dropship_mkii":
|
||||||
|
ship_name = "Federal Assault Ship"
|
||||||
|
case "empire_trader":
|
||||||
|
ship_name = "Imperial Clipper"
|
||||||
|
case "typex_2":
|
||||||
|
ship_name = "Alliance Crusader"
|
||||||
|
case "typex_3":
|
||||||
|
ship_name = "Alliance Challenger"
|
||||||
|
case "federation_gunship":
|
||||||
|
ship_name = "Federal Gunship"
|
||||||
|
case "krait_light":
|
||||||
|
ship_name = "Krait Phantom"
|
||||||
|
case "krait_mkii":
|
||||||
|
ship_name = "Krait Mk II"
|
||||||
|
case "orca":
|
||||||
|
ship_name = "Orca"
|
||||||
|
case "ferdelance":
|
||||||
|
ship_name = "Fer-de-Lance"
|
||||||
|
case "mamba":
|
||||||
|
ship_name = "Mamba"
|
||||||
|
case "python":
|
||||||
|
ship_name = "Python"
|
||||||
|
case "type9":
|
||||||
|
ship_name = "Type-9 Heavy"
|
||||||
|
case "belugaliner":
|
||||||
|
ship_name = "Beluga Liner"
|
||||||
|
case "type9_military":
|
||||||
|
ship_name = "Type-10 Defender"
|
||||||
|
case "anaconda":
|
||||||
|
ship_name = "Anaconda"
|
||||||
|
case "federation_corvette":
|
||||||
|
ship_name = "Federal Corvette"
|
||||||
|
case "cutter":
|
||||||
|
ship_name = "Imperial Cutter"
|
||||||
|
case _: # Default case for any other value
|
||||||
|
ship_name = "Unknown Ship"
|
||||||
|
return ship_name
|
||||||
|
|
||||||
|
def callback(result):
|
||||||
|
logger.info(f'Callback: {result}')
|
||||||
|
if result == dsdk.Result.ok:
|
||||||
|
logger.info("Successfully set the activity!")
|
||||||
|
elif result == dsdk.Result.transaction_aborted:
|
||||||
|
logger.warning(f'Transaction aborted due to SDK shutting down: {result}')
|
||||||
|
else:
|
||||||
|
logger.error(f'Error in callback: {result}')
|
||||||
|
raise Exception(result)
|
||||||
|
|
||||||
|
|
||||||
|
def update_presence():
|
||||||
|
if isinstance(appversion, str):
|
||||||
|
core_version = semantic_version.Version(appversion)
|
||||||
|
|
||||||
|
elif callable(appversion):
|
||||||
|
core_version = appversion()
|
||||||
|
|
||||||
|
logger.info(f'Core EDMC version: {core_version}')
|
||||||
|
if core_version < semantic_version.Version('5.0.0-beta1'):
|
||||||
|
logger.info('EDMC core version is before 5.0.0-beta1')
|
||||||
|
if config.getint("disable_presence") == 0:
|
||||||
|
this.activity.state = this.presence_state
|
||||||
|
this.activity.details = this.presence_details
|
||||||
|
this.activity.assets.large_image = this.presence_large_image
|
||||||
|
this.activity.assets.large_text = this.presence_large_text
|
||||||
|
this.activity.assets.small_image = this.presence_small_image
|
||||||
|
this.activity.assets.small_text = this.presence_small_text
|
||||||
|
else:
|
||||||
|
logger.info('EDMC core version is at least 5.0.0-beta1')
|
||||||
|
if config.get_int("disable_presence") == 0:
|
||||||
|
this.activity.state = this.presence_state
|
||||||
|
this.activity.details = this.presence_details
|
||||||
|
this.activity.assets.large_image = this.presence_large_image
|
||||||
|
this.activity.assets.large_text = this.presence_large_text
|
||||||
|
this.activity.assets.small_image = this.presence_small_image
|
||||||
|
this.activity.assets.small_text = this.presence_small_text
|
||||||
|
|
||||||
|
this.activity.timestamps.start = int(this.time_start)
|
||||||
|
this.activity_manager.update_activity(this.activity, callback)
|
||||||
|
|
||||||
|
|
||||||
|
def plugin_prefs(parent, cmdr, is_beta):
|
||||||
|
"""
|
||||||
|
Return a TK Frame for adding to the EDMC settings dialog.
|
||||||
|
"""
|
||||||
|
if isinstance(appversion, str):
|
||||||
|
core_version = semantic_version.Version(appversion)
|
||||||
|
|
||||||
|
elif callable(appversion):
|
||||||
|
core_version = appversion()
|
||||||
|
|
||||||
|
logger.info(f'Core EDMC version: {core_version}')
|
||||||
|
if core_version < semantic_version.Version('5.0.0-beta1'):
|
||||||
|
logger.info('EDMC core version is before 5.0.0-beta1')
|
||||||
|
this.disablePresence = tk.IntVar(value=config.get_int("disable_presence"))
|
||||||
|
else:
|
||||||
|
logger.info('EDMC core version is at least 5.0.0-beta1')
|
||||||
|
this.disablePresence = tk.IntVar(value=config.get_int("disable_presence"))
|
||||||
|
|
||||||
|
frame = nb.Frame(parent)
|
||||||
|
nb.Checkbutton(frame, text="Disable Presence", variable=this.disablePresence).grid()
|
||||||
|
nb.Label(frame, text='Version %s' % VERSION).grid(padx=10, pady=10, sticky=tk.W)
|
||||||
|
|
||||||
|
return frame
|
||||||
|
|
||||||
|
|
||||||
|
def prefs_changed(cmdr, is_beta):
|
||||||
|
"""
|
||||||
|
Save settings.
|
||||||
|
"""
|
||||||
|
config.set('disable_presence', this.disablePresence.get())
|
||||||
|
config.set('disable_presence', this.disableName.get())
|
||||||
|
update_presence()
|
||||||
|
|
||||||
|
|
||||||
|
def plugin_start3(plugin_dir):
|
||||||
|
this.plugin_dir = plugin_dir
|
||||||
|
this.discord_thread = threading.Thread(target=check_run, args=(plugin_dir,))
|
||||||
|
this.discord_thread.setDaemon(True)
|
||||||
|
this.discord_thread.start()
|
||||||
|
return 'DiscordPresenceNG'
|
||||||
|
|
||||||
|
|
||||||
|
def plugin_stop():
|
||||||
|
this.activity_manager.clear_activity(callback)
|
||||||
|
this.call_back_thread = None
|
||||||
|
|
||||||
|
|
||||||
|
def journal_entry(cmdr, is_beta, system, station, entry, state):
|
||||||
|
global planet
|
||||||
|
global landingPad
|
||||||
|
presence_state = this.presence_state
|
||||||
|
presence_details = this.presence_details
|
||||||
|
presence_largeimage = this.presence_large_image
|
||||||
|
presence_largetext = this.presence_large_text
|
||||||
|
presence_smallimage = this.presence_small_image
|
||||||
|
presence_smalltext = this.presence_small_text
|
||||||
|
if entry['event'] == 'StartUp':
|
||||||
|
presence_largetext = ship(('{ship}').format(ship=state['ShipType']))
|
||||||
|
presence_smalltext = ('CMDR {cmdr}').format(cmdr=cmdr)
|
||||||
|
presence_largeimage = ('{ship}').format(ship=state['ShipType'])
|
||||||
|
presence_smallimage = 'edlogo'
|
||||||
|
#presence_smalltext = ('{cmdr}cr').format(credits=state['Credits'])
|
||||||
|
presence_state = _('In system {system}').format(system=system)
|
||||||
|
if station is None:
|
||||||
|
presence_details = _('Flying in normal space')
|
||||||
|
else:
|
||||||
|
presence_details = _('Docked at {station}').format(station=station)
|
||||||
|
elif entry['event'] == 'Location':
|
||||||
|
presence_state = _('In system {system}').format(system=system)
|
||||||
|
if station is None:
|
||||||
|
presence_details = _('Flying in normal space')
|
||||||
|
else:
|
||||||
|
presence_details = _('Docked at {station}').format(station=station)
|
||||||
|
elif entry['event'] == 'StartJump':
|
||||||
|
presence_state = _('Jumping')
|
||||||
|
if entry['JumpType'] == 'Hyperspace':
|
||||||
|
presence_details = _('Jumping to system {system}').format(system=entry['StarSystem'])
|
||||||
|
elif entry['JumpType'] == 'Supercruise':
|
||||||
|
presence_details = _('Preparing for supercruise')
|
||||||
|
elif entry['event'] == 'SupercruiseEntry':
|
||||||
|
presence_state = _('In system {system}').format(system=system)
|
||||||
|
presence_details = _('Supercruising')
|
||||||
|
elif entry['event'] == 'SupercruiseExit':
|
||||||
|
presence_state = _('In system {system}').format(system=system)
|
||||||
|
presence_details = _('Flying in normal space')
|
||||||
|
elif entry['event'] == 'FSDJump':
|
||||||
|
presence_state = _('In system {system}').format(system=system)
|
||||||
|
presence_details = _('Supercruising')
|
||||||
|
elif entry['event'] == 'Docked':
|
||||||
|
presence_state = _('In system {system}').format(system=system)
|
||||||
|
presence_details = _('Docked at {station}').format(station=station)
|
||||||
|
elif entry['event'] == 'Undocked':
|
||||||
|
presence_state = _('In system {system}').format(system=system)
|
||||||
|
presence_details = _('Flying in normal space')
|
||||||
|
elif entry['event'] == 'ShutDown':
|
||||||
|
presence_state = _('Main Menu')
|
||||||
|
presence_details = ''
|
||||||
|
elif entry['event'] == 'DockingGranted':
|
||||||
|
landingPad = entry['LandingPad']
|
||||||
|
elif entry['event'] == 'Music':
|
||||||
|
if entry['MusicTrack'] == 'MainMenu':
|
||||||
|
presence_state = _('In Main Menu')
|
||||||
|
presence_details = ''
|
||||||
|
# Todo: This elif might not be executed on undocked. Functionality can be improved
|
||||||
|
elif entry['event'] == 'Undocked' or entry['event'] == 'DockingCancelled' or entry['event'] == 'DockingTimeout':
|
||||||
|
presence_details = _('Flying near {station}').format(station=entry['StationName'])
|
||||||
|
# Planetary events
|
||||||
|
elif entry['event'] == 'ApproachBody':
|
||||||
|
presence_details = _('Approaching {body}').format(body=entry['Body'])
|
||||||
|
planet = entry['Body']
|
||||||
|
elif entry['event'] == 'Touchdown' and entry['PlayerControlled']:
|
||||||
|
presence_details = _('Landed on {body}').format(body=planet)
|
||||||
|
elif entry['event'] == 'Liftoff' and entry['PlayerControlled']:
|
||||||
|
if entry['PlayerControlled']:
|
||||||
|
presence_details = _('Flying around {body}').format(body=planet)
|
||||||
|
else:
|
||||||
|
presence_details = _('In SRV on {body}, ship in orbit').format(body=planet)
|
||||||
|
elif entry['event'] == 'LeaveBody':
|
||||||
|
presence_details = _('Supercruising')
|
||||||
|
|
||||||
|
elif entry['event'] == 'Loadout':
|
||||||
|
presence_largeimage = ('{ship}').format(ship=state['ShipType'])
|
||||||
|
presence_largetext = ship(('{ship}').format(ship=state['ShipType']))
|
||||||
|
|
||||||
|
# EXTERNAL VEHICLE EVENTS
|
||||||
|
elif entry['event'] == 'LaunchSRV':
|
||||||
|
presence_details = _('In SRV on {body}').format(body=planet)
|
||||||
|
elif entry['event'] == 'DockSRV':
|
||||||
|
presence_details = _('Landed on {body}').format(body=planet)
|
||||||
|
|
||||||
|
if (presence_state != this.presence_state or
|
||||||
|
presence_details != this.presence_details or
|
||||||
|
presence_largeimage != this.presence_large_image or
|
||||||
|
presence_largetext != this.presence_large_text or
|
||||||
|
presence_smallimage != this.presence_small_image or
|
||||||
|
presence_smalltext != this.presence_small_text) :
|
||||||
|
this.presence_state = presence_state
|
||||||
|
this.presence_details = presence_details
|
||||||
|
this.presence_large_image = presence_largeimage
|
||||||
|
this.presence_large_text = presence_largetext
|
||||||
|
this.presence_small_image = presence_smallimage
|
||||||
|
this.presence_small_text = presence_smalltext
|
||||||
|
update_presence()
|
||||||
|
|
||||||
|
|
||||||
|
def check_run(plugin_dir):
|
||||||
|
plugin_path = join(dirname(plugin_dir), plugin_name)
|
||||||
|
retry = True
|
||||||
|
while retry:
|
||||||
|
time.sleep(1 / 10)
|
||||||
|
try:
|
||||||
|
this.app = dsdk.Discord(CLIENT_ID, dsdk.CreateFlags.no_require_discord, plugin_path)
|
||||||
|
retry = False
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
this.activity_manager = this.app.get_activity_manager()
|
||||||
|
this.activity = dsdk.Activity()
|
||||||
|
|
||||||
|
this.call_back_thread = threading.Thread(target=run_callbacks)
|
||||||
|
this.call_back_thread.setDaemon(True)
|
||||||
|
this.call_back_thread.start()
|
||||||
|
this.presence_state = _('Connecting CMDR Interface')
|
||||||
|
this.presence_details = ''
|
||||||
|
this.presence_large_image = 'edlogo'
|
||||||
|
this.presence_large_text = 'EliteRPC'
|
||||||
|
this.presence_small_image = ''
|
||||||
|
this.presence_small_text = ''
|
||||||
|
this.time_start = time.time()
|
||||||
|
|
||||||
|
this.disablePresence = None
|
||||||
|
this.disableName = None
|
||||||
|
|
||||||
|
update_presence()
|
||||||
|
|
||||||
|
|
||||||
|
def run_callbacks():
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
time.sleep(1 / 10)
|
||||||
|
this.app.run_callbacks()
|
||||||
|
except Exception:
|
||||||
|
check_run(this.plugin_dir)
|
157
py_discord_sdk/.gitignore
vendored
Normal file
157
py_discord_sdk/.gitignore
vendored
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/python,visualstudiocode
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=python,visualstudiocode
|
||||||
|
|
||||||
|
### Python ###
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Pycharm files
|
||||||
|
.idea/*
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
pip-wheel-metadata/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
pytestdebug.log
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
### VisualStudioCode ###
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
### VisualStudioCode Patch ###
|
||||||
|
# Ignore all local history of files
|
||||||
|
.history
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode
|
||||||
|
|
||||||
|
.idea/vcs.xml
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
.vscode/
|
22
py_discord_sdk/LICENSE
Normal file
22
py_discord_sdk/LICENSE
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 NathaanTFM
|
||||||
|
Copyright (c) 2020 LennyPhoenix
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
134
py_discord_sdk/README.md
Normal file
134
py_discord_sdk/README.md
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
# Discord Game SDK for Python
|
||||||
|
|
||||||
|
A Python wrapper around Discord's Game SDK.
|
||||||
|
|
||||||
|
**NOTE**: This is entirely experimental, and may not work as intended. Please report all bugs to the [GitHub issue tracker](https://github.com/LennyPhoenix/py-discord-sdk/issues).
|
||||||
|
|
||||||
|
**Credit to [NathaanTFM](https://github.com/NathaanTFM) for creating the [original library](https://github.com/NathaanTFM/discord-game-sdk-python).**
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
- Install the module:
|
||||||
|
- With `PIP`:
|
||||||
|
- Stable: `python -m pip install discordsdk`
|
||||||
|
- Latest: `python -m pip install git+https://github.com/LennyPhoenix/py-discord-sdk.git`
|
||||||
|
- With `setup.py` (latest):
|
||||||
|
- `git clone https://github.com/LennyPhoenix/py-discord-sdk.git`
|
||||||
|
- `cd py-discord-sdk`
|
||||||
|
- `python -m setup install`
|
||||||
|
- Download [Discord Game SDK (2.5.6)](https://dl-game-sdk.discordapp.net/2.5.6/discord_game_sdk.zip).
|
||||||
|
- Grab the DLL from `discord_game_sdk.zip` in the `lib` directory and put it in your project's `lib` directory.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
If you need documentation, look at [**the official Game SDK docs**](https://discord.com/developers/docs/game-sdk/sdk-starter-guide); this was made following the official documentation.
|
||||||
|
We also have some [work-in-progress wiki docs](https://github.com/LennyPhoenix/py-discord-sdk/wiki).
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Should be working:
|
||||||
|
- **ActivityManager**
|
||||||
|
- **ImageManager**
|
||||||
|
- **NetworkManager**
|
||||||
|
- **RelationshipManager**
|
||||||
|
- **StorageManager**
|
||||||
|
- **UserManager**
|
||||||
|
|
||||||
|
- Should be working, but need more testing:
|
||||||
|
- **AchievementManager** (not tested at all)
|
||||||
|
- **ApplicationManager** (especially the functions `GetTicket` and `ValidateOrExit`)
|
||||||
|
- **LobbyManager**
|
||||||
|
- **OverlayManager**
|
||||||
|
- **StoreManager** (not tested at all)
|
||||||
|
- **VoiceManager**
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
The code needs **more comments, type hinting**. You can also implement the **missing features**, or add **more tests**. Feel free to open a **pull request**!
|
||||||
|
|
||||||
|
You can also **report issues**. Just open an issue and I will look into it!
|
||||||
|
|
||||||
|
### Todo List
|
||||||
|
|
||||||
|
- Better organisation of submodules.
|
||||||
|
- CI/CD.
|
||||||
|
- Update sdk.py to use type annotations.
|
||||||
|
- Update to Discord SDK 3.2.0.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
You can find more examples in the `examples/` directory.
|
||||||
|
|
||||||
|
### Create a Discord instance
|
||||||
|
|
||||||
|
```python
|
||||||
|
import time
|
||||||
|
|
||||||
|
import discordsdk as dsdk
|
||||||
|
|
||||||
|
app = dsdk.Discord(APPLICATION_ID, dsdk.CreateFlags.default)
|
||||||
|
|
||||||
|
# Don't forget to call run_callbacks
|
||||||
|
while 1:
|
||||||
|
time.sleep(1/10)
|
||||||
|
app.run_callbacks()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get current user
|
||||||
|
|
||||||
|
```python
|
||||||
|
import time
|
||||||
|
|
||||||
|
import discordsdk as dsdk
|
||||||
|
|
||||||
|
app = dsdk.Discord(APPLICATION_ID, dsdk.CreateFlags.default)
|
||||||
|
|
||||||
|
user_manager = app.get_user_manager()
|
||||||
|
|
||||||
|
|
||||||
|
def on_curr_user_update():
|
||||||
|
user = user_manager.get_current_user()
|
||||||
|
print(f"Current user : {user.username}#{user.discriminator}")
|
||||||
|
|
||||||
|
|
||||||
|
user_manager.on_current_user_update = on_curr_user_update
|
||||||
|
|
||||||
|
# Don't forget to call run_callbacks
|
||||||
|
while 1:
|
||||||
|
time.sleep(1/10)
|
||||||
|
app.run_callbacks()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Set activity
|
||||||
|
|
||||||
|
```python
|
||||||
|
import time
|
||||||
|
|
||||||
|
import discordsdk as dsdk
|
||||||
|
|
||||||
|
app = dsdk.Discord(APPLICATION_ID, dsdk.CreateFlags.default)
|
||||||
|
|
||||||
|
activity_manager = app.get_activity_manager()
|
||||||
|
|
||||||
|
activity = dsdk.Activity()
|
||||||
|
activity.state = "Testing Game SDK"
|
||||||
|
activity.party.id = "my_super_party_id"
|
||||||
|
activity.party.size.current_size = 4
|
||||||
|
activity.party.size.max_size = 8
|
||||||
|
activity.secrets.join = "my_super_secret"
|
||||||
|
|
||||||
|
|
||||||
|
def callback(result):
|
||||||
|
if result == dsdk.Result.ok:
|
||||||
|
print("Successfully set the activity!")
|
||||||
|
else:
|
||||||
|
raise Exception(result)
|
||||||
|
|
||||||
|
|
||||||
|
activity_manager.update_activity(activity, callback)
|
||||||
|
|
||||||
|
# Don't forget to call run_callbacks
|
||||||
|
while 1:
|
||||||
|
time.sleep(1/10)
|
||||||
|
app.run_callbacks()
|
||||||
|
```
|
55
py_discord_sdk/discordsdk/__init__.py
Normal file
55
py_discord_sdk/discordsdk/__init__.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
from . import sdk
|
||||||
|
from .achievement import AchievementManager
|
||||||
|
from .activity import ActivityManager
|
||||||
|
from .application import ApplicationManager
|
||||||
|
from .discord import Discord
|
||||||
|
from .enum import (
|
||||||
|
ActivityActionType, ActivityJoinRequestReply, ActivityType, CreateFlags,
|
||||||
|
EntitlementType, ImageType, InputModeType, LobbySearchCast,
|
||||||
|
LobbySearchComparison, LobbySearchDistance, LobbyType, LogLevel,
|
||||||
|
PremiumType, RelationshipType, Result, SkuType, Status, UserFlag)
|
||||||
|
from .event import bind_events
|
||||||
|
from .exception import DiscordException, exceptions, get_exception
|
||||||
|
from .image import ImageManager
|
||||||
|
from .lobby import (LobbyManager, LobbyMemberTransaction, LobbySearchQuery,
|
||||||
|
LobbyTransaction)
|
||||||
|
from .model import (
|
||||||
|
Activity, ActivityAssets, ActivityParty, ActivitySecrets,
|
||||||
|
ActivityTimestamps, Entitlement, FileStat, ImageDimensions, ImageHandle,
|
||||||
|
InputMode, Lobby, Model, OAuth2Token, PartySize, Presence, Relationship,
|
||||||
|
Sku, SkuPrice, User, UserAchievement)
|
||||||
|
from .network import NetworkManager
|
||||||
|
from .overlay import OverlayManager
|
||||||
|
from .relationship import RelationshipManager
|
||||||
|
from .storage import StorageManager
|
||||||
|
from .store import StoreManager
|
||||||
|
from .user import UserManager
|
||||||
|
from .voice import VoiceManager
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"sdk",
|
||||||
|
"AchievementManager",
|
||||||
|
"ActivityManager",
|
||||||
|
"ApplicationManager",
|
||||||
|
"Discord",
|
||||||
|
"ActivityActionType", "ActivityJoinRequestReply", "ActivityType", "CreateFlags",
|
||||||
|
"EntitlementType", "ImageType", "InputModeType", "LobbySearchCast",
|
||||||
|
"LobbySearchComparison", "LobbySearchDistance", "LobbyType", "LogLevel",
|
||||||
|
"PremiumType", "RelationshipType", "Result", "SkuType", "Status", "UserFlag",
|
||||||
|
"bind_events",
|
||||||
|
"DiscordException", "exceptions", "get_exception",
|
||||||
|
"ImageManager",
|
||||||
|
"LobbyManager", "LobbyMemberTransaction", "LobbySearchQuery",
|
||||||
|
"LobbyTransaction",
|
||||||
|
"Activity", "ActivityAssets", "ActivityParty", "ActivitySecrets",
|
||||||
|
"ActivityTimestamps", "Entitlement", "FileStat", "ImageDimensions", "ImageHandle",
|
||||||
|
"InputMode", "Lobby", "Model", "OAuth2Token", "PartySize", "Presence", "Relationship",
|
||||||
|
"Sku", "SkuPrice", "User", "UserAchievement",
|
||||||
|
"NetworkManager",
|
||||||
|
"OverlayManager",
|
||||||
|
"RelationshipManager",
|
||||||
|
"StorageManager",
|
||||||
|
"StoreManager",
|
||||||
|
"UserManager",
|
||||||
|
"VoiceManager",
|
||||||
|
]
|
110
py_discord_sdk/discordsdk/achievement.py
Normal file
110
py_discord_sdk/discordsdk/achievement.py
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
import ctypes
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from . import sdk
|
||||||
|
from .enum import Result
|
||||||
|
from .event import bind_events
|
||||||
|
from .exception import get_exception
|
||||||
|
from .model import UserAchievement
|
||||||
|
|
||||||
|
|
||||||
|
class AchievementManager:
|
||||||
|
_internal: sdk.IDiscordAchievementManager = None
|
||||||
|
_garbage: t.List[t.Any]
|
||||||
|
_events: sdk.IDiscordAchievementEvents
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._garbage = []
|
||||||
|
self._events = bind_events(
|
||||||
|
sdk.IDiscordAchievementEvents,
|
||||||
|
self._on_user_achievement_update
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_user_achievement_update(self, event_data, user_achievement):
|
||||||
|
self.on_user_achievement_update(UserAchievement(copy=user_achievement.contents))
|
||||||
|
|
||||||
|
def set_user_achievement(
|
||||||
|
self,
|
||||||
|
achievement_id: int,
|
||||||
|
percent_complete: int,
|
||||||
|
callback: t.Callable[[Result], None]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Updates the current user's status for a given achievement.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.set_user_achievement.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.set_user_achievement(
|
||||||
|
self._internal,
|
||||||
|
achievement_id,
|
||||||
|
percent_complete,
|
||||||
|
ctypes.c_void_p(),
|
||||||
|
c_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
def fetch_user_achievements(self, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Loads a stable list of the current user's achievements to iterate over.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.fetch_user_achievements.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.fetch_user_achievements(self._internal, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def count_user_achievements(self) -> int:
|
||||||
|
"""
|
||||||
|
Counts the list of a user's achievements for iteration.
|
||||||
|
"""
|
||||||
|
count = ctypes.c_int32()
|
||||||
|
self._internal.count_user_achievements(self._internal, count)
|
||||||
|
return count.value
|
||||||
|
|
||||||
|
def get_user_achievement_at(self, index: int) -> UserAchievement:
|
||||||
|
"""
|
||||||
|
Gets the user's achievement at a given index of their list of achievements.
|
||||||
|
"""
|
||||||
|
achievement = sdk.DiscordUserAchievement()
|
||||||
|
result = Result(self._internal.get_user_achievement_at(
|
||||||
|
self._internal,
|
||||||
|
index,
|
||||||
|
achievement
|
||||||
|
))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return UserAchievement(internal=achievement)
|
||||||
|
|
||||||
|
def get_user_achievement(self, achievement_id: int) -> None:
|
||||||
|
"""
|
||||||
|
Gets the user achievement for the given achievement id.
|
||||||
|
"""
|
||||||
|
achievement = sdk.DiscordUserAchievement()
|
||||||
|
result = Result(self._internal.get_user_achievement(
|
||||||
|
self._internal,
|
||||||
|
achievement_id,
|
||||||
|
achievement
|
||||||
|
))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return UserAchievement(internal=achievement)
|
||||||
|
|
||||||
|
def on_user_achievement_update(self, achievement: UserAchievement) -> None:
|
||||||
|
"""
|
||||||
|
Fires when an achievement is updated for the currently connected user
|
||||||
|
"""
|
179
py_discord_sdk/discordsdk/activity.py
Normal file
179
py_discord_sdk/discordsdk/activity.py
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
import ctypes
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from . import sdk
|
||||||
|
from .enum import ActivityActionType, ActivityJoinRequestReply, Result
|
||||||
|
from .event import bind_events
|
||||||
|
from .model import Activity, User
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityManager:
|
||||||
|
_internal: sdk.IDiscordActivityManager = None
|
||||||
|
_garbage: t.List[t.Any]
|
||||||
|
_events: sdk.IDiscordActivityEvents
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._garbage = []
|
||||||
|
self._events = bind_events(
|
||||||
|
sdk.IDiscordActivityEvents,
|
||||||
|
self._on_activity_join,
|
||||||
|
self._on_activity_spectate,
|
||||||
|
self._on_activity_join_request,
|
||||||
|
self._on_activity_invite
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_activity_join(self, event_data, secret):
|
||||||
|
self.on_activity_join(secret.decode("utf8"))
|
||||||
|
|
||||||
|
def _on_activity_spectate(self, event_data, secret):
|
||||||
|
self.on_activity_spectate(secret.decode("utf8"))
|
||||||
|
|
||||||
|
def _on_activity_join_request(self, event_data, user):
|
||||||
|
self.on_activity_join_request(User(copy=user.contents))
|
||||||
|
|
||||||
|
def _on_activity_invite(self, event_data, type, user, activity):
|
||||||
|
self.on_activity_invite(type, User(copy=user.contents), Activity(copy=activity.contents))
|
||||||
|
|
||||||
|
def register_command(self, command: str) -> Result:
|
||||||
|
"""
|
||||||
|
Registers a command by which Discord can launch your game.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.register_command(self._internal, command.encode("utf8")))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def register_steam(self, steam_id: int) -> Result:
|
||||||
|
"""
|
||||||
|
Registers your game's Steam app id for the protocol `steam://run-game-id/<id>`.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.register_steam(self._internal, steam_id))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def update_activity(self, activity: Activity, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Set a user's presence in Discord to a new activity.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.update_activity.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.update_activity(
|
||||||
|
self._internal,
|
||||||
|
activity._internal,
|
||||||
|
ctypes.c_void_p(),
|
||||||
|
c_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
def clear_activity(self, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Clears a user's presence in Discord to make it show nothing.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.clear_activity.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.clear_activity(self._internal, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def send_request_reply(
|
||||||
|
self,
|
||||||
|
user_id: int,
|
||||||
|
reply: ActivityJoinRequestReply,
|
||||||
|
callback: t.Callable[[Result], None]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Sends a reply to an Ask to Join request.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.send_request_reply.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.send_request_reply(
|
||||||
|
self._internal,
|
||||||
|
user_id,
|
||||||
|
reply,
|
||||||
|
ctypes.c_void_p(),
|
||||||
|
c_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
def send_invite(
|
||||||
|
self,
|
||||||
|
user_id: int,
|
||||||
|
type: ActivityActionType,
|
||||||
|
content: str,
|
||||||
|
callback: t.Callable[[Result], None]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Sends a game invite to a given user.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.send_invite.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.send_invite(
|
||||||
|
self._internal,
|
||||||
|
user_id,
|
||||||
|
type,
|
||||||
|
content.encode("utf8"),
|
||||||
|
ctypes.c_void_p(),
|
||||||
|
c_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
def accept_invite(self, user_id: int, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Accepts a game invitation from a given userId.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.accept_invite.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.accept_invite(self._internal, user_id, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def on_activity_join(self, join_secret: str) -> None:
|
||||||
|
"""
|
||||||
|
Fires when a user accepts a game chat invite or receives confirmation from Asking to Join.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_activity_spectate(self, spectate_secret: str) -> None:
|
||||||
|
"""
|
||||||
|
Fires when a user accepts a spectate chat invite or clicks the Spectate button on a user's
|
||||||
|
profile.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_activity_join_request(self, user: User) -> None:
|
||||||
|
"""
|
||||||
|
Fires when a user asks to join the current user's game.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_activity_invite(self, type: ActivityActionType, user: User, activity: Activity) -> None:
|
||||||
|
"""
|
||||||
|
Fires when the user receives a join or spectate invite.
|
||||||
|
"""
|
85
py_discord_sdk/discordsdk/application.py
Normal file
85
py_discord_sdk/discordsdk/application.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import ctypes
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from . import sdk
|
||||||
|
from .enum import Result
|
||||||
|
from .model import OAuth2Token
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationManager:
|
||||||
|
_internal: sdk.IDiscordApplicationManager = None
|
||||||
|
_garbage: t.List[t.Any]
|
||||||
|
_events: sdk.IDiscordApplicationEvents = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._garbage = []
|
||||||
|
|
||||||
|
def get_current_locale(self) -> str:
|
||||||
|
"""
|
||||||
|
Get the locale the current user has Discord set to.
|
||||||
|
"""
|
||||||
|
locale = sdk.DiscordLocale()
|
||||||
|
self._internal.get_current_locale(self._internal, locale)
|
||||||
|
return locale.value.decode("utf8")
|
||||||
|
|
||||||
|
def get_current_branch(self) -> str:
|
||||||
|
"""
|
||||||
|
Get the name of pushed branch on which the game is running.
|
||||||
|
"""
|
||||||
|
branch = sdk.DiscordBranch()
|
||||||
|
self._internal.get_current_branch(self._internal, branch)
|
||||||
|
return branch.value.decode("utf8")
|
||||||
|
|
||||||
|
def get_oauth2_token(
|
||||||
|
self,
|
||||||
|
callback: t.Callable[[Result, t.Optional[OAuth2Token]], None]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Retrieve an oauth2 bearer token for the current user.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) and OAuth2Token (str) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result, oauth2_token):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
if result == Result.ok:
|
||||||
|
callback(result, OAuth2Token(copy=oauth2_token.contents))
|
||||||
|
else:
|
||||||
|
callback(result, None)
|
||||||
|
|
||||||
|
c_callback = self._internal.get_oauth2_token.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.get_oauth2_token(self._internal, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def validate_or_exit(self, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Checks if the current user has the entitlement to run this game.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.validate_or_exit.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.validate_or_exit(self._internal, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def get_ticket(self, callback: t.Callable[[Result, t.Optional[str]], None]) -> None:
|
||||||
|
"""
|
||||||
|
Get the signed app ticket for the current user.
|
||||||
|
|
||||||
|
Returns discordsdk.Enum.Result (int) and str via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result, data):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
if result == Result.ok:
|
||||||
|
callback(result, data.contents.value.decode("utf8"))
|
||||||
|
else:
|
||||||
|
callback(result, None)
|
||||||
|
|
||||||
|
c_callback = self._internal.get_ticket.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.get_ticket(self._internal, ctypes.c_void_p(), c_callback)
|
202
py_discord_sdk/discordsdk/discord.py
Normal file
202
py_discord_sdk/discordsdk/discord.py
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
import ctypes
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from . import sdk
|
||||||
|
from .achievement import AchievementManager
|
||||||
|
from .activity import ActivityManager
|
||||||
|
from .application import ApplicationManager
|
||||||
|
from .enum import CreateFlags, LogLevel, Result
|
||||||
|
from .exception import get_exception
|
||||||
|
from .image import ImageManager
|
||||||
|
from .lobby import LobbyManager
|
||||||
|
from .network import NetworkManager
|
||||||
|
from .overlay import OverlayManager
|
||||||
|
from .relationship import RelationshipManager
|
||||||
|
from .storage import StorageManager
|
||||||
|
from .store import StoreManager
|
||||||
|
from .user import UserManager
|
||||||
|
from .voice import VoiceManager
|
||||||
|
|
||||||
|
|
||||||
|
class Discord:
|
||||||
|
_garbage: t.List[t.Any]
|
||||||
|
|
||||||
|
core: sdk.IDiscordCore = None
|
||||||
|
|
||||||
|
def __init__(self, client_id: int, flags: CreateFlags, dll_base_path: str):
|
||||||
|
self._garbage = []
|
||||||
|
|
||||||
|
self._activity_manager = ActivityManager()
|
||||||
|
self._relationship_manager = RelationshipManager()
|
||||||
|
self._image_manager = ImageManager()
|
||||||
|
self._user_manager = UserManager()
|
||||||
|
self._lobby_manager = LobbyManager()
|
||||||
|
self._network_manager = NetworkManager()
|
||||||
|
self._overlay_manager = OverlayManager()
|
||||||
|
self._application_manager = ApplicationManager()
|
||||||
|
self._storage_manager = StorageManager()
|
||||||
|
self._store_manager = StoreManager()
|
||||||
|
self._voice_manager = VoiceManager()
|
||||||
|
self._achievement_manager = AchievementManager()
|
||||||
|
|
||||||
|
version = sdk.DiscordVersion(2)
|
||||||
|
|
||||||
|
params = sdk.DiscordCreateParams()
|
||||||
|
params.client_id = client_id
|
||||||
|
params.flags = flags
|
||||||
|
|
||||||
|
sdk.DiscordCreateParamsSetDefault(params)
|
||||||
|
params.activity_events = self._activity_manager._events
|
||||||
|
params.relationship_events = self._relationship_manager._events
|
||||||
|
params.image_events = self._image_manager._events
|
||||||
|
params.user_events = self._user_manager._events
|
||||||
|
params.lobby_events = self._lobby_manager._events
|
||||||
|
params.network_events = self._network_manager._events
|
||||||
|
params.overlay_events = self._overlay_manager._events
|
||||||
|
params.application_events = self._application_manager._events
|
||||||
|
params.storage_events = self._storage_manager._events
|
||||||
|
params.store_events = self._store_manager._events
|
||||||
|
params.voice_events = self._voice_manager._events
|
||||||
|
params.achievement_events = self._achievement_manager._events
|
||||||
|
|
||||||
|
pointer = ctypes.POINTER(sdk.IDiscordCore)()
|
||||||
|
|
||||||
|
result = Result(sdk.build_sdk(dll_base_path)(version, ctypes.pointer(params), ctypes.pointer(pointer)))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
self.core = pointer.contents
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self.core:
|
||||||
|
self.core.destroy(self.core)
|
||||||
|
self.core = None
|
||||||
|
|
||||||
|
def set_log_hook(self, min_level: LogLevel, hook: t.Callable[[LogLevel, str], None]) -> None:
|
||||||
|
"""
|
||||||
|
Registers a logging callback function with the minimum level of message to receive.
|
||||||
|
"""
|
||||||
|
def c_hook(hook_data, level, message):
|
||||||
|
level = LogLevel(level)
|
||||||
|
hook(level, message.decode("utf8"))
|
||||||
|
|
||||||
|
c_hook = self.core.set_log_hook.argtypes[-1](c_hook)
|
||||||
|
self._garbage.append(c_hook)
|
||||||
|
|
||||||
|
self.core.set_log_hook(self.core, min_level.value, ctypes.c_void_p(), c_hook)
|
||||||
|
|
||||||
|
def run_callbacks(self) -> None:
|
||||||
|
"""
|
||||||
|
Runs all pending SDK callbacks.
|
||||||
|
"""
|
||||||
|
result = Result(self.core.run_callbacks(self.core))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def get_activity_manager(self) -> ActivityManager:
|
||||||
|
"""
|
||||||
|
Fetches an instance of the manager for interfacing with activies in the SDK.
|
||||||
|
"""
|
||||||
|
if not self._activity_manager._internal:
|
||||||
|
self._activity_manager._internal = self.core.get_activity_manager(self.core).contents
|
||||||
|
|
||||||
|
return self._activity_manager
|
||||||
|
|
||||||
|
def get_relationship_manager(self) -> RelationshipManager:
|
||||||
|
"""
|
||||||
|
Fetches an instance of the manager for interfacing with relationships in the SDK.
|
||||||
|
"""
|
||||||
|
if not self._relationship_manager._internal:
|
||||||
|
self._relationship_manager._internal = self.core.get_relationship_manager(self.core).contents # noqa: E501
|
||||||
|
|
||||||
|
return self._relationship_manager
|
||||||
|
|
||||||
|
def get_image_manager(self) -> ImageManager:
|
||||||
|
"""
|
||||||
|
Fetches an instance of the manager for interfacing with images in the SDK.
|
||||||
|
"""
|
||||||
|
if not self._image_manager._internal:
|
||||||
|
self._image_manager._internal = self.core.get_image_manager(self.core).contents
|
||||||
|
|
||||||
|
return self._image_manager
|
||||||
|
|
||||||
|
def get_user_manager(self) -> UserManager:
|
||||||
|
"""
|
||||||
|
Fetches an instance of the manager for interfacing with users in the SDK.
|
||||||
|
"""
|
||||||
|
if not self._user_manager._internal:
|
||||||
|
self._user_manager._internal = self.core.get_user_manager(self.core).contents
|
||||||
|
|
||||||
|
return self._user_manager
|
||||||
|
|
||||||
|
def get_lobby_manager(self) -> LobbyManager:
|
||||||
|
"""
|
||||||
|
Fetches an instance of the manager for interfacing with lobbies in the SDK.
|
||||||
|
"""
|
||||||
|
if not self._lobby_manager._internal:
|
||||||
|
self._lobby_manager._internal = self.core.get_lobby_manager(self.core).contents
|
||||||
|
|
||||||
|
return self._lobby_manager
|
||||||
|
|
||||||
|
def get_network_manager(self) -> NetworkManager:
|
||||||
|
"""
|
||||||
|
Fetches an instance of the manager for interfacing with networking in the SDK.
|
||||||
|
"""
|
||||||
|
if not self._network_manager._internal:
|
||||||
|
self._network_manager._internal = self.core.get_network_manager(self.core).contents
|
||||||
|
|
||||||
|
return self._network_manager
|
||||||
|
|
||||||
|
def get_overlay_manager(self) -> OverlayManager:
|
||||||
|
"""
|
||||||
|
Fetches an instance of the manager for interfacing with the overlay in the SDK.
|
||||||
|
"""
|
||||||
|
if not self._overlay_manager._internal:
|
||||||
|
self._overlay_manager._internal = self.core.get_overlay_manager(self.core).contents
|
||||||
|
|
||||||
|
return self._overlay_manager
|
||||||
|
|
||||||
|
def get_application_manager(self) -> ApplicationManager:
|
||||||
|
"""
|
||||||
|
Fetches an instance of the manager for interfacing with applications in the SDK.
|
||||||
|
"""
|
||||||
|
if not self._application_manager._internal:
|
||||||
|
self._application_manager._internal = self.core.get_application_manager(self.core).contents # noqa: E501
|
||||||
|
|
||||||
|
return self._application_manager
|
||||||
|
|
||||||
|
def get_storage_manager(self) -> StorageManager:
|
||||||
|
"""
|
||||||
|
Fetches an instance of the manager for interfacing with storage in the SDK.
|
||||||
|
"""
|
||||||
|
if not self._storage_manager._internal:
|
||||||
|
self._storage_manager._internal = self.core.get_storage_manager(self.core).contents
|
||||||
|
|
||||||
|
return self._storage_manager
|
||||||
|
|
||||||
|
def get_store_manager(self) -> StoreManager:
|
||||||
|
"""
|
||||||
|
Fetches an instance of the manager for interfacing with SKUs and Entitlements in the SDK.
|
||||||
|
"""
|
||||||
|
if not self._store_manager._internal:
|
||||||
|
self._store_manager._internal = self.core.get_store_manager(self.core).contents
|
||||||
|
|
||||||
|
return self._store_manager
|
||||||
|
|
||||||
|
def get_voice_manager(self) -> VoiceManager:
|
||||||
|
"""
|
||||||
|
Fetches an instance of the manager for interfacing with voice chat in the SDK.
|
||||||
|
"""
|
||||||
|
if not self._voice_manager._internal:
|
||||||
|
self._voice_manager._internal = self.core.get_voice_manager(self.core).contents
|
||||||
|
|
||||||
|
return self._voice_manager
|
||||||
|
|
||||||
|
def get_achievement_manager(self) -> AchievementManager:
|
||||||
|
"""
|
||||||
|
Fetches an instance of the manager for interfacing with achievements in the SDK.
|
||||||
|
"""
|
||||||
|
if not self._achievement_manager._internal:
|
||||||
|
self._achievement_manager._internal = self.core.get_achievement_manager(self.core).contents # noqa: E501
|
||||||
|
|
||||||
|
return self._achievement_manager
|
166
py_discord_sdk/discordsdk/enum.py
Normal file
166
py_discord_sdk/discordsdk/enum.py
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 6):
|
||||||
|
from enum import IntEnum, IntFlag
|
||||||
|
else:
|
||||||
|
from enum import IntEnum, IntEnum as IntFlag
|
||||||
|
|
||||||
|
|
||||||
|
class Result(IntEnum):
|
||||||
|
ok = 0
|
||||||
|
service_unavailable = 1
|
||||||
|
invalid_version = 2
|
||||||
|
lock_failed = 3
|
||||||
|
internal_error = 4
|
||||||
|
invalid_payload = 5
|
||||||
|
invalid_command = 6
|
||||||
|
invalid_permissions = 7
|
||||||
|
not_fetched = 8
|
||||||
|
not_found = 9
|
||||||
|
conflict = 10
|
||||||
|
invalid_secret = 11
|
||||||
|
invalid_join_secret = 12
|
||||||
|
no_eligible_activity = 13
|
||||||
|
invalid_invite = 14
|
||||||
|
not_authenticated = 15
|
||||||
|
invalid_access_token = 16
|
||||||
|
application_mismatch = 17
|
||||||
|
invalid_data_url = 18
|
||||||
|
invalid_base_64 = 19
|
||||||
|
not_filtered = 20
|
||||||
|
lobby_full = 21
|
||||||
|
invalid_lobby_secret = 22
|
||||||
|
invalid_filename = 23
|
||||||
|
invalid_file_size = 24
|
||||||
|
invalid_entitlement = 25
|
||||||
|
not_installed = 26
|
||||||
|
not_running = 27
|
||||||
|
insufficient_buffer = 28
|
||||||
|
purchase_canceled = 29
|
||||||
|
invalid_guild = 30
|
||||||
|
invalid_event = 31
|
||||||
|
invalid_channel = 32
|
||||||
|
invalid_origin = 33
|
||||||
|
rate_limited = 34
|
||||||
|
oauth2_error = 35
|
||||||
|
select_channel_timeout = 36
|
||||||
|
get_guild_timeout = 37
|
||||||
|
select_voice_force_required = 38
|
||||||
|
capture_shortcut_already_listening = 39
|
||||||
|
unauthorized_for_achievement = 40
|
||||||
|
invalid_gift_code = 41
|
||||||
|
purchase_error = 42
|
||||||
|
transaction_aborted = 43
|
||||||
|
drawing_init_failed = 44
|
||||||
|
|
||||||
|
|
||||||
|
class LogLevel(IntEnum):
|
||||||
|
error = 0
|
||||||
|
warning = 1
|
||||||
|
info = 2
|
||||||
|
debug = 3
|
||||||
|
|
||||||
|
|
||||||
|
class CreateFlags(IntFlag):
|
||||||
|
default = 0
|
||||||
|
no_require_discord = 1
|
||||||
|
|
||||||
|
|
||||||
|
class UserFlag(IntFlag):
|
||||||
|
partner = 2
|
||||||
|
hype_squad_events = 4
|
||||||
|
hype_squad_house_1 = 64
|
||||||
|
hype_squad_house_2 = 128
|
||||||
|
hype_squad_house_3 = 256
|
||||||
|
|
||||||
|
|
||||||
|
class PremiumType(IntEnum):
|
||||||
|
none_ = 0
|
||||||
|
tier_1 = 1
|
||||||
|
tier_2 = 2
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityType(IntEnum):
|
||||||
|
playing = 0
|
||||||
|
streaming = 1
|
||||||
|
listening = 2
|
||||||
|
custom = 4
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityJoinRequestReply(IntEnum):
|
||||||
|
no = 0
|
||||||
|
yes = 1
|
||||||
|
ignore = 2
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityActionType(IntEnum):
|
||||||
|
join = 1
|
||||||
|
spectate = 2
|
||||||
|
|
||||||
|
|
||||||
|
class RelationshipType(IntEnum):
|
||||||
|
none_ = 0
|
||||||
|
friend = 1
|
||||||
|
blocked = 2
|
||||||
|
pending_incoming = 3
|
||||||
|
pending_outgoing = 4
|
||||||
|
implicit = 5
|
||||||
|
|
||||||
|
|
||||||
|
class Status(IntEnum):
|
||||||
|
offline = 0
|
||||||
|
online = 1
|
||||||
|
idle = 2
|
||||||
|
do_not_disturb = 3
|
||||||
|
|
||||||
|
|
||||||
|
class ImageType(IntEnum):
|
||||||
|
user = 0
|
||||||
|
|
||||||
|
|
||||||
|
class LobbyType(IntEnum):
|
||||||
|
private = 1
|
||||||
|
public = 2
|
||||||
|
|
||||||
|
|
||||||
|
class LobbySearchComparison(IntEnum):
|
||||||
|
LessThanOrEqual = -2
|
||||||
|
LessThan = -1
|
||||||
|
Equal = 0
|
||||||
|
GreaterThan = 1
|
||||||
|
GreaterThanOrEqual = 2
|
||||||
|
NotEqual = 3
|
||||||
|
|
||||||
|
|
||||||
|
class LobbySearchCast(IntEnum):
|
||||||
|
String = 1
|
||||||
|
Number = 2
|
||||||
|
|
||||||
|
|
||||||
|
class LobbySearchDistance(IntEnum):
|
||||||
|
Local = 0
|
||||||
|
Default = 1
|
||||||
|
Extended = 2
|
||||||
|
Global = 3
|
||||||
|
|
||||||
|
|
||||||
|
class InputModeType(IntEnum):
|
||||||
|
VoiceActivity = 0
|
||||||
|
PushToTalk = 1
|
||||||
|
|
||||||
|
|
||||||
|
class SkuType(IntEnum):
|
||||||
|
Application = 1
|
||||||
|
DLC = 2
|
||||||
|
Consumable = 3
|
||||||
|
Bundle = 4
|
||||||
|
|
||||||
|
|
||||||
|
class EntitlementType(IntEnum):
|
||||||
|
Purchase = 1
|
||||||
|
PremiumSubscription = 2
|
||||||
|
DeveloperGift = 3
|
||||||
|
TestModePurchase = 4
|
||||||
|
FreePurchase = 5
|
||||||
|
UserGift = 6
|
||||||
|
PremiumPurchase = 7
|
11
py_discord_sdk/discordsdk/event.py
Normal file
11
py_discord_sdk/discordsdk/event.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import ctypes
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
|
||||||
|
def bind_events(structure: ctypes.Structure, *methods: t.List[t.Callable[..., None]]):
|
||||||
|
contents = structure()
|
||||||
|
for index, (name, func) in enumerate(structure._fields_):
|
||||||
|
setattr(contents, name, func(methods[index]))
|
||||||
|
|
||||||
|
pointer = ctypes.pointer(contents)
|
||||||
|
return pointer
|
19
py_discord_sdk/discordsdk/exception.py
Normal file
19
py_discord_sdk/discordsdk/exception.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
from .enum import Result
|
||||||
|
|
||||||
|
|
||||||
|
class DiscordException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
exceptions = {}
|
||||||
|
|
||||||
|
# we dynamically create the exceptions
|
||||||
|
for res in Result:
|
||||||
|
exception = type(res.name, (DiscordException,), {})
|
||||||
|
|
||||||
|
globals()[res.name] = exception
|
||||||
|
exceptions[res] = exception
|
||||||
|
|
||||||
|
|
||||||
|
def get_exception(result):
|
||||||
|
return exceptions.get(result, DiscordException)("result " + str(result.value))
|
71
py_discord_sdk/discordsdk/image.py
Normal file
71
py_discord_sdk/discordsdk/image.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import ctypes
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from . import sdk
|
||||||
|
from .enum import Result
|
||||||
|
from .exception import get_exception
|
||||||
|
from .model import ImageDimensions, ImageHandle
|
||||||
|
|
||||||
|
|
||||||
|
class ImageManager:
|
||||||
|
_internal: sdk.IDiscordImageManager = None
|
||||||
|
_garbage: t.List[t.Any]
|
||||||
|
_events: sdk.IDiscordImageEvents = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._garbage = []
|
||||||
|
|
||||||
|
def fetch(
|
||||||
|
self,
|
||||||
|
handle: ImageHandle,
|
||||||
|
refresh: bool,
|
||||||
|
callback: t.Callable[[Result, t.Optional[ImageHandle]], None]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Prepares an image to later retrieve data about it.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) and ImageHandle via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result, handle):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
if result == Result.ok:
|
||||||
|
callback(result, ImageHandle(internal=handle))
|
||||||
|
else:
|
||||||
|
callback(result, None)
|
||||||
|
|
||||||
|
c_callback = self._internal.fetch.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.fetch(
|
||||||
|
self._internal,
|
||||||
|
handle._internal,
|
||||||
|
refresh,
|
||||||
|
ctypes.c_void_p(),
|
||||||
|
c_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_dimensions(self, handle: ImageHandle) -> ImageDimensions:
|
||||||
|
"""
|
||||||
|
Gets the dimension for the given user's avatar's source image
|
||||||
|
"""
|
||||||
|
dimensions = sdk.DiscordImageDimensions()
|
||||||
|
result = Result(self._internal.get_dimensions(self._internal, handle._internal, dimensions))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return ImageDimensions(internal=dimensions)
|
||||||
|
|
||||||
|
def get_data(self, handle: ImageHandle) -> bytes:
|
||||||
|
"""
|
||||||
|
Gets the image data for a given user's avatar.
|
||||||
|
"""
|
||||||
|
dimensions = self.get_dimensions(handle)
|
||||||
|
buffer = (ctypes.c_uint8 * (dimensions.width * dimensions.height * 4))()
|
||||||
|
|
||||||
|
result = Result(self._internal.get_data(
|
||||||
|
self._internal, handle._internal, buffer, len(buffer)))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return bytes(buffer)
|
801
py_discord_sdk/discordsdk/lobby.py
Normal file
801
py_discord_sdk/discordsdk/lobby.py
Normal file
|
@ -0,0 +1,801 @@
|
||||||
|
import ctypes
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from . import sdk
|
||||||
|
from .enum import (
|
||||||
|
LobbySearchCast, LobbySearchComparison, LobbySearchDistance, LobbyType,
|
||||||
|
Result)
|
||||||
|
from .event import bind_events
|
||||||
|
from .exception import get_exception
|
||||||
|
from .model import Lobby, User
|
||||||
|
|
||||||
|
|
||||||
|
class LobbyTransaction:
|
||||||
|
_internal: sdk.IDiscordLobbyTransaction
|
||||||
|
|
||||||
|
def __init__(self, internal: sdk.IDiscordLobbyTransaction):
|
||||||
|
self._internal = internal
|
||||||
|
|
||||||
|
def set_type(self, type: LobbyType) -> None:
|
||||||
|
"""
|
||||||
|
Marks a lobby as private or public.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.set_type(self._internal, type))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def set_owner(self, user_id: int) -> None:
|
||||||
|
"""
|
||||||
|
Sets a new owner for the lobby.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.set_owner(self._internal, user_id))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def set_capacity(self, capacity: int) -> None:
|
||||||
|
"""
|
||||||
|
Sets a new capacity for the lobby.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.set_capacity(self._internal, capacity))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def set_metadata(self, key: str, value: str) -> None:
|
||||||
|
"""
|
||||||
|
Sets metadata value under a given key name for the lobby.
|
||||||
|
"""
|
||||||
|
metadata_key = sdk.DiscordMetadataKey()
|
||||||
|
metadata_key.value = key.encode("utf8")
|
||||||
|
|
||||||
|
metadata_value = sdk.DiscordMetadataValue()
|
||||||
|
metadata_value.value = value.encode("utf8")
|
||||||
|
|
||||||
|
result = Result(self._internal.set_metadata(self._internal, metadata_key, metadata_value))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def delete_metadata(self, key: str) -> None:
|
||||||
|
"""
|
||||||
|
Deletes the lobby metadata for a key.
|
||||||
|
"""
|
||||||
|
metadata_key = sdk.DiscordMetadataKey()
|
||||||
|
metadata_key.value = key.encode("utf8")
|
||||||
|
|
||||||
|
result = Result(self._internal.delete_metadata(self._internal, metadata_key))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def set_locked(self, locked: bool) -> None:
|
||||||
|
"""
|
||||||
|
Sets the lobby to locked or unlocked.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.set_locked(self._internal, locked))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
|
||||||
|
class LobbyMemberTransaction:
|
||||||
|
_internal: sdk.IDiscordLobbyMemberTransaction
|
||||||
|
|
||||||
|
def __init__(self, internal: sdk.IDiscordLobbyMemberTransaction):
|
||||||
|
self._internal = internal
|
||||||
|
|
||||||
|
def set_metadata(self, key: str, value: str) -> None:
|
||||||
|
"""
|
||||||
|
Sets metadata value under a given key name for the current user.
|
||||||
|
"""
|
||||||
|
metadata_key = sdk.DiscordMetadataKey()
|
||||||
|
metadata_key.value = key.encode("utf8")
|
||||||
|
|
||||||
|
metadata_value = sdk.DiscordMetadataValue()
|
||||||
|
metadata_value.value = value.encode("utf8")
|
||||||
|
|
||||||
|
result = Result(self._internal.set_metadata(self._internal, metadata_key, metadata_value))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def delete_metadata(self, key: str) -> None:
|
||||||
|
"""
|
||||||
|
Sets metadata value under a given key name for the current user.
|
||||||
|
"""
|
||||||
|
metadata_key = sdk.DiscordMetadataKey()
|
||||||
|
metadata_key.value = key.encode("utf8")
|
||||||
|
|
||||||
|
result = Result(self._internal.delete_metadata(self._internal, metadata_key))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
|
||||||
|
class LobbySearchQuery:
|
||||||
|
_internal: sdk.IDiscordLobbySearchQuery
|
||||||
|
|
||||||
|
def __init__(self, internal: sdk.IDiscordLobbySearchQuery):
|
||||||
|
self._internal = internal
|
||||||
|
|
||||||
|
def filter(
|
||||||
|
self,
|
||||||
|
key: str,
|
||||||
|
comp: LobbySearchComparison,
|
||||||
|
cast: LobbySearchCast,
|
||||||
|
value: str
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Filters lobbies based on metadata comparison.
|
||||||
|
"""
|
||||||
|
metadata_key = sdk.DiscordMetadataKey()
|
||||||
|
metadata_key.value = key.encode("utf8")
|
||||||
|
|
||||||
|
metadata_value = sdk.DiscordMetadataValue()
|
||||||
|
metadata_value.value = value.encode("utf8")
|
||||||
|
|
||||||
|
result = Result(self._internal.filter(
|
||||||
|
self._internal,
|
||||||
|
metadata_key,
|
||||||
|
comp,
|
||||||
|
cast,
|
||||||
|
metadata_value
|
||||||
|
))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def sort(self, key: str, cast: LobbySearchCast, value: str) -> None:
|
||||||
|
"""
|
||||||
|
Sorts the filtered lobbies based on "near-ness" to a given value.
|
||||||
|
"""
|
||||||
|
metadata_key = sdk.DiscordMetadataKey()
|
||||||
|
metadata_key.value = key.encode("utf8")
|
||||||
|
|
||||||
|
metadata_value = sdk.DiscordMetadataValue()
|
||||||
|
metadata_value.value = value.encode("utf8")
|
||||||
|
|
||||||
|
result = Result(self._internal.sort(self._internal, metadata_key, cast, metadata_value))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def limit(self, limit: int) -> None:
|
||||||
|
"""
|
||||||
|
Limits the number of lobbies returned in a search.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.limit(self._internal, limit))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def distance(self, distance: LobbySearchDistance) -> None:
|
||||||
|
"""
|
||||||
|
Filters lobby results to within certain regions relative to the user's location.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.distance(self._internal, distance))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
|
||||||
|
class LobbyManager:
|
||||||
|
_internal: sdk.IDiscordLobbyManager = None
|
||||||
|
_garbage: t.List[t.Any]
|
||||||
|
_events: sdk.IDiscordLobbyEvents
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._garbage = []
|
||||||
|
self._events = bind_events(
|
||||||
|
sdk.IDiscordLobbyEvents,
|
||||||
|
self._on_lobby_update,
|
||||||
|
self._on_lobby_delete,
|
||||||
|
self._on_member_connect,
|
||||||
|
self._on_member_update,
|
||||||
|
self._on_member_disconnect,
|
||||||
|
self._on_lobby_message,
|
||||||
|
self._on_speaking,
|
||||||
|
self._on_network_message
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_lobby_update(self, event_data, lobby_id):
|
||||||
|
self.on_lobby_update(lobby_id)
|
||||||
|
|
||||||
|
def _on_lobby_delete(self, event_data, lobby_id, reason):
|
||||||
|
self.on_lobby_delete(lobby_id, reason)
|
||||||
|
|
||||||
|
def _on_member_connect(self, event_data, lobby_id, user_id):
|
||||||
|
self.on_member_connect(lobby_id, user_id)
|
||||||
|
|
||||||
|
def _on_member_update(self, event_data, lobby_id, user_id):
|
||||||
|
self.on_member_update(lobby_id, user_id)
|
||||||
|
|
||||||
|
def _on_member_disconnect(self, event_data, lobby_id, user_id):
|
||||||
|
self.on_member_disconnect(lobby_id, user_id)
|
||||||
|
|
||||||
|
def _on_lobby_message(self, event_data, lobby_id, user_id, data, data_length):
|
||||||
|
message = bytes(data[:data_length]).decode("utf8")
|
||||||
|
self.on_lobby_message(lobby_id, user_id, message)
|
||||||
|
|
||||||
|
def _on_speaking(self, event_data, lobby_id, user_id, speaking):
|
||||||
|
self.on_speaking(lobby_id, user_id, speaking)
|
||||||
|
|
||||||
|
def _on_network_message(self, event_data, lobby_id, user_id, channel_id, data, data_length):
|
||||||
|
data = bytes(data[:data_length])
|
||||||
|
self.on_network_message(lobby_id, user_id, channel_id, data)
|
||||||
|
|
||||||
|
def get_lobby_create_transaction(self) -> LobbyTransaction:
|
||||||
|
"""
|
||||||
|
Gets a Lobby transaction used for creating a new lobby
|
||||||
|
"""
|
||||||
|
transaction = ctypes.POINTER(sdk.IDiscordLobbyTransaction)()
|
||||||
|
result = Result(self._internal.get_lobby_create_transaction(self._internal, transaction))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return LobbyTransaction(internal=transaction.contents)
|
||||||
|
|
||||||
|
def get_lobby_update_transaction(self, lobby_id: int) -> LobbyTransaction:
|
||||||
|
"""
|
||||||
|
Gets a lobby transaction used for updating an existing lobby.
|
||||||
|
"""
|
||||||
|
transaction = ctypes.POINTER(sdk.IDiscordLobbyTransaction)()
|
||||||
|
result = Result(self._internal.get_lobby_update_transaction(
|
||||||
|
self._internal,
|
||||||
|
lobby_id,
|
||||||
|
transaction
|
||||||
|
))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return LobbyTransaction(internal=transaction.contents)
|
||||||
|
|
||||||
|
def get_member_update_transaction(self, lobby_id: int, user_id: int) -> LobbyMemberTransaction:
|
||||||
|
"""
|
||||||
|
Gets a new member transaction for a lobby member in a given lobby.
|
||||||
|
"""
|
||||||
|
transaction = ctypes.POINTER(sdk.IDiscordLobbyMemberTransaction)()
|
||||||
|
result = Result(self._internal.get_member_update_transaction(
|
||||||
|
self._internal, lobby_id, user_id, transaction))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return LobbyMemberTransaction(internal=transaction.contents)
|
||||||
|
|
||||||
|
def create_lobby(
|
||||||
|
self,
|
||||||
|
transaction: LobbyTransaction,
|
||||||
|
callback: t.Callable[[Result, t.Optional[Lobby]], None]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Creates a lobby.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) and Lobby via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result, lobby):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
if result == Result.ok:
|
||||||
|
callback(result, Lobby(copy=lobby.contents))
|
||||||
|
else:
|
||||||
|
callback(result, None)
|
||||||
|
|
||||||
|
c_callback = self._internal.create_lobby.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.create_lobby(
|
||||||
|
self._internal,
|
||||||
|
transaction._internal,
|
||||||
|
ctypes.c_void_p(),
|
||||||
|
c_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_lobby(
|
||||||
|
self,
|
||||||
|
lobby_id: int,
|
||||||
|
transaction: LobbyTransaction,
|
||||||
|
callback: t.Callable[[Result], None]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Updates a lobby with data from the given transaction.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.update_lobby.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.update_lobby(
|
||||||
|
self._internal,
|
||||||
|
lobby_id,
|
||||||
|
transaction._internal,
|
||||||
|
ctypes.c_void_p(),
|
||||||
|
c_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete_lobby(self, lobby_id: int, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Deletes a given lobby.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.delete_lobby.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.delete_lobby(self._internal, lobby_id, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def connect_lobby(
|
||||||
|
self,
|
||||||
|
lobby_id: int,
|
||||||
|
lobby_secret: str,
|
||||||
|
callback: t.Callable[[Result], None]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Connects the current user to a given lobby.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result, lobby):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
if result == Result.ok:
|
||||||
|
callback(result, Lobby(copy=lobby.contents))
|
||||||
|
else:
|
||||||
|
callback(result, None)
|
||||||
|
|
||||||
|
c_callback = self._internal.connect_lobby.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
_lobby_secret = sdk.DiscordLobbySecret()
|
||||||
|
_lobby_secret.value = lobby_secret.encode("utf8")
|
||||||
|
|
||||||
|
self._internal.connect_lobby(
|
||||||
|
self._internal,
|
||||||
|
lobby_id,
|
||||||
|
_lobby_secret,
|
||||||
|
ctypes.c_void_p(),
|
||||||
|
c_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
def connect_lobby_with_activity_secret(
|
||||||
|
self,
|
||||||
|
activity_secret: str,
|
||||||
|
callback: t.Callable[[Result, t.Optional[Lobby]], None]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Connects the current user to a lobby; requires the special activity secret from the lobby
|
||||||
|
which is a concatenated lobby_id and secret.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result, lobby):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
if result == Result.ok:
|
||||||
|
callback(result, Lobby(copy=lobby.contents))
|
||||||
|
else:
|
||||||
|
callback(result, None)
|
||||||
|
|
||||||
|
c_callback = self._internal.connect_lobby_with_activity_secret.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
_activity_secret = sdk.DiscordLobbySecret()
|
||||||
|
_activity_secret.value = activity_secret.encode("utf8")
|
||||||
|
|
||||||
|
self._internal.connect_lobby_with_activity_secret(
|
||||||
|
self._internal,
|
||||||
|
_activity_secret,
|
||||||
|
ctypes.c_void_p(),
|
||||||
|
c_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_lobby_activity_secret(self, lobby_id: int) -> str:
|
||||||
|
"""
|
||||||
|
Gets the special activity secret for a given lobby.
|
||||||
|
"""
|
||||||
|
lobby_secret = sdk.DiscordLobbySecret()
|
||||||
|
|
||||||
|
result = self._internal.get_lobby_activity_secret(self._internal, lobby_id, lobby_secret)
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return lobby_secret.value.decode("utf8")
|
||||||
|
|
||||||
|
def disconnect_lobby(self, lobby_id: int, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Disconnects the current user from a lobby.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.disconnect_lobby.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.disconnect_lobby(self._internal, lobby_id, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def get_lobby(self, lobby_id: int) -> Lobby:
|
||||||
|
"""
|
||||||
|
Gets the lobby object for a given lobby id.
|
||||||
|
"""
|
||||||
|
lobby = sdk.DiscordLobby()
|
||||||
|
|
||||||
|
result = Result(self._internal.get_lobby(self._internal, lobby_id, lobby))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return Lobby(internal=lobby)
|
||||||
|
|
||||||
|
def lobby_metadata_count(self, lobby_id: int) -> int:
|
||||||
|
"""
|
||||||
|
Returns the number of metadata key/value pairs on a given lobby.
|
||||||
|
"""
|
||||||
|
count = ctypes.c_int32()
|
||||||
|
|
||||||
|
result = Result(self._internal.lobby_metadata_count(self._internal, lobby_id, count))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return count.value
|
||||||
|
|
||||||
|
def get_lobby_metadata_key(self, lobby_id: int, index: int) -> str:
|
||||||
|
"""
|
||||||
|
Returns the key for the lobby metadata at the given index.
|
||||||
|
"""
|
||||||
|
metadata_key = sdk.DiscordMetadataKey()
|
||||||
|
|
||||||
|
result = Result(self._internal.get_lobby_metadata_key(
|
||||||
|
self._internal,
|
||||||
|
lobby_id,
|
||||||
|
index,
|
||||||
|
metadata_key
|
||||||
|
))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return metadata_key.value.decode("utf8")
|
||||||
|
|
||||||
|
def get_lobby_metadata_value(self, lobby_id: int, key: str) -> str:
|
||||||
|
"""
|
||||||
|
Returns lobby metadata value for a given key and id.
|
||||||
|
"""
|
||||||
|
metadata_key = sdk.DiscordMetadataKey()
|
||||||
|
metadata_key.value = key.encode("utf8")
|
||||||
|
|
||||||
|
metadata_value = sdk.DiscordMetadataValue()
|
||||||
|
|
||||||
|
result = Result(self._internal.get_lobby_metadata_value(
|
||||||
|
self._internal,
|
||||||
|
lobby_id,
|
||||||
|
metadata_key,
|
||||||
|
metadata_value
|
||||||
|
))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return metadata_value.value.decode("utf8")
|
||||||
|
|
||||||
|
def member_count(self, lobby_id: int) -> int:
|
||||||
|
"""
|
||||||
|
Get the number of members in a lobby.
|
||||||
|
"""
|
||||||
|
count = ctypes.c_int32()
|
||||||
|
|
||||||
|
result = Result(self._internal.member_count(self._internal, lobby_id, count))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return count.value
|
||||||
|
|
||||||
|
def get_member_user_id(self, lobby_id: int, index: int) -> int:
|
||||||
|
"""
|
||||||
|
Gets the user id of the lobby member at the given index.
|
||||||
|
"""
|
||||||
|
user_id = sdk.DiscordUserId()
|
||||||
|
|
||||||
|
result = Result(self._internal.get_member_user_id(self._internal, lobby_id, index, user_id))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return user_id.value
|
||||||
|
|
||||||
|
def get_member_user(self, lobby_id: int, user_id: int) -> User:
|
||||||
|
"""
|
||||||
|
Gets the user object for a given user id.
|
||||||
|
"""
|
||||||
|
user = sdk.DiscordUser()
|
||||||
|
|
||||||
|
result = Result(self._internal.get_member_user(self._internal, lobby_id, user_id, user))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return User(internal=user)
|
||||||
|
|
||||||
|
def member_metadata_count(self, lobby_id: int, user_id: int) -> int:
|
||||||
|
"""
|
||||||
|
Gets the number of metadata key/value pairs for the given lobby member.
|
||||||
|
"""
|
||||||
|
count = ctypes.c_int32()
|
||||||
|
|
||||||
|
result = Result(self._internal.member_metadata_count(
|
||||||
|
self._internal,
|
||||||
|
lobby_id,
|
||||||
|
user_id,
|
||||||
|
count
|
||||||
|
))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return count.value
|
||||||
|
|
||||||
|
def get_member_metadata_key(self, lobby_id: int, user_id: int, index: int) -> str:
|
||||||
|
"""
|
||||||
|
Gets the key for the lobby metadata at the given index on a lobby member.
|
||||||
|
"""
|
||||||
|
metadata_key = sdk.DiscordMetadataKey()
|
||||||
|
|
||||||
|
result = Result(self._internal.get_member_metadata_key(
|
||||||
|
self._internal,
|
||||||
|
lobby_id,
|
||||||
|
user_id,
|
||||||
|
index,
|
||||||
|
metadata_key
|
||||||
|
))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return metadata_key.value.decode("utf8")
|
||||||
|
|
||||||
|
def get_member_metadata_value(self, lobby_id: int, user_id: int, key: str) -> str:
|
||||||
|
"""
|
||||||
|
Returns user metadata for a given key.
|
||||||
|
"""
|
||||||
|
metadata_key = sdk.DiscordMetadataKey()
|
||||||
|
metadata_key.value = key.encode("utf8")
|
||||||
|
|
||||||
|
metadata_value = sdk.DiscordMetadataValue()
|
||||||
|
|
||||||
|
result = Result(self._internal.get_member_metadata_value(
|
||||||
|
self._internal,
|
||||||
|
lobby_id,
|
||||||
|
user_id,
|
||||||
|
metadata_key,
|
||||||
|
metadata_value
|
||||||
|
))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return metadata_value.value.decode("utf8")
|
||||||
|
|
||||||
|
def update_member(
|
||||||
|
self,
|
||||||
|
lobby_id: int,
|
||||||
|
user_id: int,
|
||||||
|
transaction: LobbyMemberTransaction,
|
||||||
|
callback: t.Callable[[Result], None]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Updates lobby member info for a given member of the lobby.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.update_member.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.update_member(
|
||||||
|
self._internal,
|
||||||
|
lobby_id,
|
||||||
|
user_id,
|
||||||
|
transaction._internal,
|
||||||
|
ctypes.c_void_p(),
|
||||||
|
c_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
def send_lobby_message(
|
||||||
|
self,
|
||||||
|
lobby_id: int,
|
||||||
|
data: str,
|
||||||
|
callback: t.Callable[[Result], None]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Sends a message to the lobby on behalf of the current user.
|
||||||
|
|
||||||
|
Returns discordsdk.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.send_lobby_message.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
data = data.encode("utf8")
|
||||||
|
data = (ctypes.c_uint8 * len(data))(*data)
|
||||||
|
self._internal.send_lobby_message(
|
||||||
|
self._internal, lobby_id, data, len(data), ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def get_search_query(self) -> LobbySearchQuery:
|
||||||
|
"""
|
||||||
|
Creates a search object to search available lobbies.
|
||||||
|
"""
|
||||||
|
search_query = (ctypes.POINTER(sdk.IDiscordLobbySearchQuery))()
|
||||||
|
result = Result(self._internal.get_search_query(self._internal, ctypes.byref(search_query)))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return LobbySearchQuery(internal=search_query.contents)
|
||||||
|
|
||||||
|
def search(self, search: LobbySearchQuery, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Searches available lobbies based on the search criteria chosen in the LobbySearchQuery
|
||||||
|
member functions.
|
||||||
|
|
||||||
|
Lobbies that meet the criteria are then globally filtered, and can be accessed via
|
||||||
|
iteration with lobby_count() and get_lobby_id(). The callback fires when the list of lobbies
|
||||||
|
is stable and ready for iteration.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.search.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.search(self._internal, search._internal, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def lobby_count(self) -> int:
|
||||||
|
"""
|
||||||
|
Get the number of lobbies that match the search.
|
||||||
|
"""
|
||||||
|
count = ctypes.c_int32()
|
||||||
|
self._internal.lobby_count(self._internal, count)
|
||||||
|
return count.value
|
||||||
|
|
||||||
|
def get_lobby_id(self, index: int) -> int:
|
||||||
|
"""
|
||||||
|
Returns the id for the lobby at the given index.
|
||||||
|
"""
|
||||||
|
|
||||||
|
lobby_id = sdk.DiscordLobbyId()
|
||||||
|
|
||||||
|
result = Result(self._internal.get_lobby_id(self._internal, index, lobby_id))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return lobby_id.value
|
||||||
|
|
||||||
|
def connect_voice(self, lobby_id: int, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Connects to the voice channel of the current lobby.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.connect_voice.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.connect_voice(self._internal, lobby_id, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def disconnect_voice(self, lobby_id: int, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Disconnects from the voice channel of a given lobby.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.disconnect_voice.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.disconnect_voice(self._internal, lobby_id, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def on_lobby_update(self, lobby_id: int) -> None:
|
||||||
|
"""
|
||||||
|
Fires when a lobby is updated.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_lobby_delete(self, lobby_id: int, reason: str) -> None:
|
||||||
|
"""
|
||||||
|
Fired when a lobby is deleted.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_member_connect(self, lobby_id: int, user_id: int) -> None:
|
||||||
|
"""
|
||||||
|
Fires when a new member joins the lobby.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_member_update(self, lobby_id: int, user_id: int) -> None:
|
||||||
|
"""
|
||||||
|
Fires when data for a lobby member is updated.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_member_disconnect(self, lobby_id: int, user_id: int) -> None:
|
||||||
|
"""
|
||||||
|
Fires when a member leaves the lobby.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_lobby_message(self, lobby_id: int, user_id: int, message: str) -> None:
|
||||||
|
"""
|
||||||
|
Fires when a message is sent to the lobby.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_speaking(self, lobby_id: int, user_id: int, speaking: bool) -> None:
|
||||||
|
"""
|
||||||
|
Fires when a user connected to voice starts or stops speaking.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def connect_network(self, lobby_id: int) -> None:
|
||||||
|
"""
|
||||||
|
Connects to the networking layer for the given lobby ID.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.connect_network(self._internal, lobby_id))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def disconnect_network(self, lobby_id: int) -> None:
|
||||||
|
"""
|
||||||
|
Disconnects from the networking layer for the given lobby ID.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.disconnect_network(self._internal, lobby_id))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def flush_network(self) -> None:
|
||||||
|
"""
|
||||||
|
Flushes the network. Call this when you're done sending messages.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.flush_network(self._internal))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def open_network_channel(self, lobby_id: int, channel_id: int, reliable: bool) -> None:
|
||||||
|
"""
|
||||||
|
Opens a network channel to all users in a lobby on the given channel number. No need to
|
||||||
|
iterate over everyone!
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.open_network_channel(
|
||||||
|
self._internal,
|
||||||
|
lobby_id,
|
||||||
|
channel_id,
|
||||||
|
reliable
|
||||||
|
))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def send_network_message(
|
||||||
|
self,
|
||||||
|
lobby_id: int,
|
||||||
|
user_id: int,
|
||||||
|
channel_id: int,
|
||||||
|
data: bytes
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Sends a network message to the given user ID that is a member of the given lobby ID over
|
||||||
|
the given channel ID.
|
||||||
|
"""
|
||||||
|
data = (ctypes.c_uint8 * len(data))(*data)
|
||||||
|
result = Result(self._internal.send_network_message(
|
||||||
|
self._internal,
|
||||||
|
lobby_id,
|
||||||
|
user_id,
|
||||||
|
channel_id,
|
||||||
|
data,
|
||||||
|
len(data)
|
||||||
|
))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def on_network_message(self, lobby_id: int, user_id: int, channel_id: int, data: bytes) -> None:
|
||||||
|
"""
|
||||||
|
Fires when the user receives a message from the lobby's networking layer.
|
||||||
|
"""
|
329
py_discord_sdk/discordsdk/model.py
Normal file
329
py_discord_sdk/discordsdk/model.py
Normal file
|
@ -0,0 +1,329 @@
|
||||||
|
import ctypes
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from . import sdk
|
||||||
|
from .enum import (EntitlementType, ImageType, InputModeType, LobbyType,
|
||||||
|
RelationshipType, SkuType, Status)
|
||||||
|
|
||||||
|
|
||||||
|
class Model:
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self._internal = kwargs.get("internal", self._struct_())
|
||||||
|
if "copy" in kwargs:
|
||||||
|
ctypes.memmove(
|
||||||
|
ctypes.byref(self._internal),
|
||||||
|
ctypes.byref(kwargs["copy"]),
|
||||||
|
ctypes.sizeof(self._struct_)
|
||||||
|
)
|
||||||
|
|
||||||
|
self._fields = {}
|
||||||
|
|
||||||
|
for field, ftype in self._fields_:
|
||||||
|
self._fields[field] = ftype
|
||||||
|
if issubclass(ftype, Model):
|
||||||
|
setattr(self, "_" + field, ftype(internal=getattr(self._internal, field)))
|
||||||
|
|
||||||
|
def __getattribute__(self, key):
|
||||||
|
if key.startswith("_"):
|
||||||
|
return super().__getattribute__(key)
|
||||||
|
else:
|
||||||
|
ftype = self._fields[key]
|
||||||
|
value = getattr(self._internal, key)
|
||||||
|
if ftype == int:
|
||||||
|
return int(value)
|
||||||
|
elif ftype == str:
|
||||||
|
return value.decode("utf8")
|
||||||
|
elif ftype == bool:
|
||||||
|
return bool(value)
|
||||||
|
elif issubclass(ftype, Model):
|
||||||
|
return getattr(self, "_" + key)
|
||||||
|
elif issubclass(ftype, Enum):
|
||||||
|
return ftype(int(value))
|
||||||
|
else:
|
||||||
|
raise TypeError(ftype)
|
||||||
|
|
||||||
|
def __setattr__(self, key, value):
|
||||||
|
if key.startswith("_"):
|
||||||
|
super().__setattr__(key, value)
|
||||||
|
else:
|
||||||
|
ftype = self._fields[key]
|
||||||
|
if ftype == int:
|
||||||
|
value = int(value)
|
||||||
|
setattr(self._internal, key, value)
|
||||||
|
elif ftype == str:
|
||||||
|
value = value.encode("utf8")
|
||||||
|
setattr(self._internal, key, value)
|
||||||
|
elif ftype == bool:
|
||||||
|
value = bool(value)
|
||||||
|
setattr(self._internal, key, value)
|
||||||
|
elif issubclass(ftype, Model):
|
||||||
|
setattr(self, "_" + key, value)
|
||||||
|
setattr(self._internal, key, value._internal)
|
||||||
|
elif issubclass(ftype, Enum):
|
||||||
|
setattr(self._internal, key, value.value)
|
||||||
|
else:
|
||||||
|
raise TypeError(ftype)
|
||||||
|
|
||||||
|
def __dir__(self):
|
||||||
|
return super().__dir__() + list(self._fields.keys())
|
||||||
|
|
||||||
|
|
||||||
|
class User(Model):
|
||||||
|
_struct_ = sdk.DiscordUser
|
||||||
|
_fields_ = [
|
||||||
|
("id", int),
|
||||||
|
("username", str),
|
||||||
|
("discriminator", str),
|
||||||
|
("avatar", str),
|
||||||
|
("bot", bool),
|
||||||
|
]
|
||||||
|
|
||||||
|
id: int
|
||||||
|
username: str
|
||||||
|
discriminator: str
|
||||||
|
avatar: str
|
||||||
|
bot: str
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityTimestamps(Model):
|
||||||
|
_struct_ = sdk.DiscordActivityTimestamps
|
||||||
|
_fields_ = [
|
||||||
|
("start", int),
|
||||||
|
("end", int),
|
||||||
|
]
|
||||||
|
|
||||||
|
start: int
|
||||||
|
end: int
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityAssets(Model):
|
||||||
|
_struct_ = sdk.DiscordActivityAssets
|
||||||
|
_fields_ = [
|
||||||
|
("large_image", str),
|
||||||
|
("large_text", str),
|
||||||
|
("small_image", str),
|
||||||
|
("small_text", str),
|
||||||
|
]
|
||||||
|
|
||||||
|
large_image: str
|
||||||
|
large_text: str
|
||||||
|
small_image: str
|
||||||
|
small_text: str
|
||||||
|
|
||||||
|
|
||||||
|
class PartySize(Model):
|
||||||
|
_struct_ = sdk.DiscordPartySize
|
||||||
|
_fields_ = [
|
||||||
|
("current_size", int),
|
||||||
|
("max_size", int),
|
||||||
|
]
|
||||||
|
|
||||||
|
current_size: int
|
||||||
|
max_size: int
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityParty(Model):
|
||||||
|
_struct_ = sdk.DiscordActivityParty
|
||||||
|
_fields_ = [
|
||||||
|
("id", str),
|
||||||
|
("size", PartySize),
|
||||||
|
]
|
||||||
|
|
||||||
|
id: str
|
||||||
|
size: PartySize
|
||||||
|
|
||||||
|
|
||||||
|
class ActivitySecrets(Model):
|
||||||
|
_struct_ = sdk.DiscordActivitySecrets
|
||||||
|
_fields_ = [
|
||||||
|
("match", str),
|
||||||
|
("join", str),
|
||||||
|
("spectate", str),
|
||||||
|
]
|
||||||
|
|
||||||
|
match: str
|
||||||
|
join: str
|
||||||
|
spectate: str
|
||||||
|
|
||||||
|
|
||||||
|
class Activity(Model):
|
||||||
|
_struct_ = sdk.DiscordActivity
|
||||||
|
_fields_ = [
|
||||||
|
("application_id", int),
|
||||||
|
("name", str),
|
||||||
|
("state", str),
|
||||||
|
("details", str),
|
||||||
|
("timestamps", ActivityTimestamps),
|
||||||
|
("assets", ActivityAssets),
|
||||||
|
("party", ActivityParty),
|
||||||
|
("secrets", ActivitySecrets),
|
||||||
|
("instance", bool),
|
||||||
|
]
|
||||||
|
|
||||||
|
application_id: int
|
||||||
|
name: str
|
||||||
|
state: str
|
||||||
|
details: str
|
||||||
|
timestamps: ActivityTimestamps
|
||||||
|
assets: ActivityAssets
|
||||||
|
party: ActivityParty
|
||||||
|
secrets: ActivitySecrets
|
||||||
|
instance: bool
|
||||||
|
|
||||||
|
|
||||||
|
class Presence(Model):
|
||||||
|
_struct_ = sdk.DiscordPresence
|
||||||
|
_fields_ = [
|
||||||
|
("status", Status),
|
||||||
|
("activity", Activity),
|
||||||
|
]
|
||||||
|
|
||||||
|
status: Status
|
||||||
|
activity: Activity
|
||||||
|
|
||||||
|
|
||||||
|
class Relationship(Model):
|
||||||
|
_struct_ = sdk.DiscordRelationship
|
||||||
|
_fields_ = [
|
||||||
|
("type", RelationshipType),
|
||||||
|
("user", User),
|
||||||
|
("presence", Presence),
|
||||||
|
]
|
||||||
|
|
||||||
|
type: RelationshipType
|
||||||
|
user: User
|
||||||
|
presence: Presence
|
||||||
|
|
||||||
|
|
||||||
|
class ImageDimensions(Model):
|
||||||
|
_struct_ = sdk.DiscordImageDimensions
|
||||||
|
_fields_ = [
|
||||||
|
("width", int),
|
||||||
|
("height", int),
|
||||||
|
]
|
||||||
|
|
||||||
|
width: int
|
||||||
|
height: int
|
||||||
|
|
||||||
|
|
||||||
|
class ImageHandle(Model):
|
||||||
|
_struct_ = sdk.DiscordImageHandle
|
||||||
|
_fields_ = [
|
||||||
|
("type", ImageType),
|
||||||
|
("id", int),
|
||||||
|
("size", int),
|
||||||
|
]
|
||||||
|
|
||||||
|
type: ImageType
|
||||||
|
id: int
|
||||||
|
size: int
|
||||||
|
|
||||||
|
|
||||||
|
class OAuth2Token(Model):
|
||||||
|
_struct_ = sdk.DiscordOAuth2Token
|
||||||
|
_fields_ = [
|
||||||
|
("access_token", str),
|
||||||
|
("scopes", str),
|
||||||
|
("expires", int),
|
||||||
|
]
|
||||||
|
|
||||||
|
access_token: str
|
||||||
|
scopes: str
|
||||||
|
expires: str
|
||||||
|
|
||||||
|
|
||||||
|
class Lobby(Model):
|
||||||
|
_struct_ = sdk.DiscordLobby
|
||||||
|
_fields_ = [
|
||||||
|
("id", int),
|
||||||
|
("type", LobbyType),
|
||||||
|
("owner_id", int),
|
||||||
|
("secret", str),
|
||||||
|
("capacity", int),
|
||||||
|
("locked", bool),
|
||||||
|
]
|
||||||
|
|
||||||
|
id: int
|
||||||
|
type: LobbyType
|
||||||
|
owner_id: int
|
||||||
|
secret: str
|
||||||
|
capacity: int
|
||||||
|
locked: bool
|
||||||
|
|
||||||
|
|
||||||
|
class InputMode(Model):
|
||||||
|
_struct_ = sdk.DiscordInputMode
|
||||||
|
_fields_ = [
|
||||||
|
("type", InputModeType),
|
||||||
|
("shortcut", str),
|
||||||
|
]
|
||||||
|
|
||||||
|
type: InputModeType
|
||||||
|
shortcut: str
|
||||||
|
|
||||||
|
|
||||||
|
class FileStat(Model):
|
||||||
|
_struct_ = sdk.DiscordFileStat
|
||||||
|
_fields_ = [
|
||||||
|
("filename", str),
|
||||||
|
("size", int),
|
||||||
|
("last_modified", int),
|
||||||
|
]
|
||||||
|
|
||||||
|
filename: str
|
||||||
|
size: int
|
||||||
|
last_modified: int
|
||||||
|
|
||||||
|
|
||||||
|
class UserAchievement(Model):
|
||||||
|
_struct_ = sdk.DiscordUserAchievement
|
||||||
|
_fields_ = [
|
||||||
|
("user_id", str),
|
||||||
|
("achievement_id", int),
|
||||||
|
("percent_complete", int),
|
||||||
|
("unlocked_at", str),
|
||||||
|
]
|
||||||
|
|
||||||
|
user_id: str
|
||||||
|
achievement_id: int
|
||||||
|
percent_complete: int
|
||||||
|
unlocked_at: str
|
||||||
|
|
||||||
|
|
||||||
|
class SkuPrice(Model):
|
||||||
|
_struct_ = sdk.DiscordSkuPrice
|
||||||
|
_fields_ = [
|
||||||
|
("amount", int),
|
||||||
|
("currency", str),
|
||||||
|
]
|
||||||
|
|
||||||
|
amount: int
|
||||||
|
currency: str
|
||||||
|
|
||||||
|
|
||||||
|
class Sku(Model):
|
||||||
|
_struct_ = sdk.DiscordSku
|
||||||
|
_fields_ = [
|
||||||
|
("id", int),
|
||||||
|
("type", SkuType),
|
||||||
|
("name", str),
|
||||||
|
("price", SkuPrice),
|
||||||
|
]
|
||||||
|
|
||||||
|
id: int
|
||||||
|
type: SkuType
|
||||||
|
name: str
|
||||||
|
price: SkuPrice
|
||||||
|
|
||||||
|
|
||||||
|
class Entitlement(Model):
|
||||||
|
_struct_ = sdk.DiscordEntitlement
|
||||||
|
_fields_ = [
|
||||||
|
("id", int),
|
||||||
|
("type", EntitlementType),
|
||||||
|
("sku_id", int),
|
||||||
|
]
|
||||||
|
|
||||||
|
id: int
|
||||||
|
type: EntitlementType
|
||||||
|
sku_id: int
|
111
py_discord_sdk/discordsdk/network.py
Normal file
111
py_discord_sdk/discordsdk/network.py
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
import ctypes
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from . import sdk
|
||||||
|
from .enum import Result
|
||||||
|
from .event import bind_events
|
||||||
|
from .exception import get_exception
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkManager:
|
||||||
|
_internal: sdk.IDiscordNetworkManager = None
|
||||||
|
_garbage: t.List[t.Any]
|
||||||
|
_events: sdk.IDiscordNetworkEvents
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._events = bind_events(
|
||||||
|
sdk.IDiscordNetworkEvents,
|
||||||
|
self._on_message,
|
||||||
|
self._on_route_update
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_message(self, event_data, peer_id, channel_id, data, data_length):
|
||||||
|
data = bytes(data[:data_length])
|
||||||
|
self.on_message(peer_id, channel_id, data)
|
||||||
|
|
||||||
|
def _on_route_update(self, event_data, route_data):
|
||||||
|
self.on_route_update(route_data.decode("utf8"))
|
||||||
|
|
||||||
|
def get_peer_id(self) -> int:
|
||||||
|
"""
|
||||||
|
Get the networking peer_id for the current user, allowing other users to send packets to
|
||||||
|
them.
|
||||||
|
"""
|
||||||
|
peerId = sdk.DiscordNetworkPeerId()
|
||||||
|
self._internal.get_peer_id(self._internal, peerId)
|
||||||
|
return peerId.value
|
||||||
|
|
||||||
|
def flush(self) -> None:
|
||||||
|
"""
|
||||||
|
Flushes the network.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.flush(self._internal))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def open_channel(self, peer_id: int, channel_id: int, reliable: bool) -> None:
|
||||||
|
"""
|
||||||
|
Opens a channel to a user with their given peer_id on the given channel number.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.open_channel(self._internal, peer_id, channel_id, reliable))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def open_peer(self, peer_id: int, route: str) -> None:
|
||||||
|
"""
|
||||||
|
Opens a network connection to another Discord user.
|
||||||
|
"""
|
||||||
|
route_data = ctypes.create_string_buffer(route.encode("utf8"))
|
||||||
|
result = Result(self._internal.open_peer(self._internal, peer_id, route_data))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def update_peer(self, peer_id: int, route: str) -> None:
|
||||||
|
"""
|
||||||
|
Updates the network connection to another Discord user.
|
||||||
|
"""
|
||||||
|
route_data = ctypes.create_string_buffer(route.encode("utf8"))
|
||||||
|
result = Result(self._internal.update_peer(self._internal, peer_id, route_data))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def send_message(self, peer_id: int, channel_id: int, data: bytes) -> None:
|
||||||
|
"""
|
||||||
|
Sends data to a given peer_id through the given channel.
|
||||||
|
"""
|
||||||
|
data = (ctypes.c_uint8 * len(data))(*data)
|
||||||
|
result = Result(self._internal.send_message(
|
||||||
|
self._internal,
|
||||||
|
peer_id,
|
||||||
|
channel_id,
|
||||||
|
data,
|
||||||
|
len(data)
|
||||||
|
))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def close_channel(self, peer_id: int, channel_id: int) -> None:
|
||||||
|
"""
|
||||||
|
Close the connection to a given user by peer_id on the given channel.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.close_channel(self._internal, peer_id, channel_id))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def close_peer(self, peer_id: int) -> None:
|
||||||
|
"""
|
||||||
|
Disconnects the network session to another Discord user.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.close_peer(self._internal, peer_id))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def on_message(self, peer_id: int, channel_id: int, data: bytes) -> None:
|
||||||
|
"""
|
||||||
|
Fires when you receive data from another user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_route_update(self, route: str) -> None:
|
||||||
|
"""
|
||||||
|
Fires when your networking route has changed.
|
||||||
|
"""
|
104
py_discord_sdk/discordsdk/overlay.py
Normal file
104
py_discord_sdk/discordsdk/overlay.py
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
import ctypes
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from . import sdk
|
||||||
|
from .enum import ActivityActionType, Result
|
||||||
|
from .event import bind_events
|
||||||
|
|
||||||
|
|
||||||
|
class OverlayManager:
|
||||||
|
_internal: sdk.IDiscordOverlayManager = None
|
||||||
|
_garbage: t.List[t.Any]
|
||||||
|
_events: sdk.IDiscordOverlayEvents
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._garbage = []
|
||||||
|
self._events = bind_events(
|
||||||
|
sdk.IDiscordOverlayEvents,
|
||||||
|
self._on_toggle
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_toggle(self, event_data, locked):
|
||||||
|
self.on_toggle(locked)
|
||||||
|
|
||||||
|
def is_enabled(self) -> bool:
|
||||||
|
"""
|
||||||
|
Check whether the user has the overlay enabled or disabled.
|
||||||
|
"""
|
||||||
|
enabled = ctypes.c_bool()
|
||||||
|
self._internal.is_enabled(self._internal, enabled)
|
||||||
|
return enabled.value
|
||||||
|
|
||||||
|
def is_locked(self) -> bool:
|
||||||
|
"""
|
||||||
|
Check if the overlay is currently locked or unlocked
|
||||||
|
"""
|
||||||
|
locked = ctypes.c_bool()
|
||||||
|
self._internal.is_locked(self._internal, locked)
|
||||||
|
return locked.value
|
||||||
|
|
||||||
|
def set_locked(self, locked: bool, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Locks or unlocks input in the overlay.
|
||||||
|
"""
|
||||||
|
def c_callback(event_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.set_locked.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.set_locked(self._internal, locked, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def open_activity_invite(
|
||||||
|
self,
|
||||||
|
type: ActivityActionType,
|
||||||
|
callback: t.Callable[[Result], None]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Opens the overlay modal for sending game invitations to users, channels, and servers.
|
||||||
|
"""
|
||||||
|
def c_callback(event_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.open_activity_invite.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.open_activity_invite(self._internal, type, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def open_guild_invite(self, code: str, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Opens the overlay modal for joining a Discord guild, given its invite code.
|
||||||
|
"""
|
||||||
|
def c_callback(event_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.open_guild_invite.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
code = ctypes.c_char_p(code.encode("utf8"))
|
||||||
|
self._internal.open_guild_invite(self._internal, code, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def open_voice_settings(self, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Opens the overlay widget for voice settings for the currently connected application.
|
||||||
|
"""
|
||||||
|
def c_callback(event_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.open_voice_settings.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.open_voice_settings(self._internal, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def on_toggle(self, locked: bool) -> None:
|
||||||
|
"""
|
||||||
|
Fires when the overlay is locked or unlocked (a.k.a. opened or closed)
|
||||||
|
"""
|
84
py_discord_sdk/discordsdk/relationship.py
Normal file
84
py_discord_sdk/discordsdk/relationship.py
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
import ctypes
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from . import sdk
|
||||||
|
from .enum import Result
|
||||||
|
from .event import bind_events
|
||||||
|
from .exception import get_exception
|
||||||
|
from .model import Relationship
|
||||||
|
|
||||||
|
|
||||||
|
class RelationshipManager:
|
||||||
|
_internal: sdk.IDiscordRelationshipManager = None
|
||||||
|
_garbage: t.List[t.Any]
|
||||||
|
_events: sdk.IDiscordRelationshipEvents
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._garbage = []
|
||||||
|
self._events = bind_events(
|
||||||
|
sdk.IDiscordRelationshipEvents,
|
||||||
|
self._on_refresh,
|
||||||
|
self._on_relationship_update
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_refresh(self, event_data):
|
||||||
|
self.on_refresh()
|
||||||
|
|
||||||
|
def _on_relationship_update(self, event_data, relationship):
|
||||||
|
self.on_relationship_update(Relationship(copy=relationship.contents))
|
||||||
|
|
||||||
|
def filter(self, filter: t.Callable[[Relationship], None]) -> None:
|
||||||
|
"""
|
||||||
|
Filters a user's relationship list by a boolean condition.
|
||||||
|
"""
|
||||||
|
def c_filter(filter_data, relationship):
|
||||||
|
return bool(filter(Relationship(copy=relationship.contents)))
|
||||||
|
|
||||||
|
c_filter = self._internal.filter.argtypes[-1](c_filter)
|
||||||
|
|
||||||
|
self._internal.filter(self._internal, ctypes.c_void_p(), c_filter)
|
||||||
|
|
||||||
|
def get(self, user_id: int) -> Relationship:
|
||||||
|
"""
|
||||||
|
Get the relationship between the current user and a given user by id.
|
||||||
|
"""
|
||||||
|
pointer = sdk.DiscordRelationship()
|
||||||
|
result = Result(self._internal.get(self._internal, user_id, pointer))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return Relationship(internal=pointer)
|
||||||
|
|
||||||
|
def get_at(self, index: int) -> Relationship:
|
||||||
|
"""
|
||||||
|
Get the relationship at a given index when iterating over a list of relationships.
|
||||||
|
"""
|
||||||
|
pointer = sdk.DiscordRelationship()
|
||||||
|
result = Result(self._internal.get_at(self._internal, index, pointer))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return Relationship(internal=pointer)
|
||||||
|
|
||||||
|
def count(self) -> int:
|
||||||
|
"""
|
||||||
|
Get the number of relationships that match your filter.
|
||||||
|
"""
|
||||||
|
count = ctypes.c_int32()
|
||||||
|
result = Result(self._internal.count(self._internal, count))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return count.value
|
||||||
|
|
||||||
|
def on_refresh(self) -> None:
|
||||||
|
"""
|
||||||
|
Fires at initialization when Discord has cached a snapshot of the current status of all
|
||||||
|
your relationships.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_relationship_update(self, relationship: Relationship) -> None:
|
||||||
|
"""
|
||||||
|
Fires when a relationship in the filtered list changes, like an updated presence or user
|
||||||
|
attribute.
|
||||||
|
"""
|
1538
py_discord_sdk/discordsdk/sdk.py
Normal file
1538
py_discord_sdk/discordsdk/sdk.py
Normal file
File diff suppressed because it is too large
Load diff
200
py_discord_sdk/discordsdk/storage.py
Normal file
200
py_discord_sdk/discordsdk/storage.py
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
import ctypes
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from . import sdk
|
||||||
|
from .enum import Result
|
||||||
|
from .exception import get_exception
|
||||||
|
from .model import FileStat
|
||||||
|
|
||||||
|
|
||||||
|
class StorageManager:
|
||||||
|
_internal: sdk.IDiscordStorageManager = None
|
||||||
|
_garbage: t.List[t.Any]
|
||||||
|
_events: sdk.IDiscordStorageEvents = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._garbage = []
|
||||||
|
|
||||||
|
def get_path(self) -> str:
|
||||||
|
"""
|
||||||
|
Returns the filepath to which Discord saves files if you were to use the SDK's storage
|
||||||
|
manager.
|
||||||
|
"""
|
||||||
|
path = sdk.DiscordPath()
|
||||||
|
|
||||||
|
result = Result(self._internal.get_path(self._internal, path))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return path.value.decode("utf8")
|
||||||
|
|
||||||
|
def read(self, name: str) -> bytes:
|
||||||
|
"""
|
||||||
|
Reads data synchronously from the game's allocated save file.
|
||||||
|
"""
|
||||||
|
# we need the file stat for this one, as length-fixed buffers does not exist in python
|
||||||
|
file_stat = self.stat(name)
|
||||||
|
file_size = file_stat.Size
|
||||||
|
|
||||||
|
name = ctypes.c_char_p(name.encode("utf8"))
|
||||||
|
buffer = (ctypes.c_uint8 * file_size)()
|
||||||
|
read = ctypes.c_uint32()
|
||||||
|
|
||||||
|
result = Result(self._internal.read(self._internal, name, buffer, len(buffer), read))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
if read.value != file_size:
|
||||||
|
print("discord/storage.py: warning: attempting to read " +
|
||||||
|
str(file_size) + " bytes, but read " + str(read.value))
|
||||||
|
|
||||||
|
return bytes(buffer[:read.value])
|
||||||
|
|
||||||
|
def read_async(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
callback: t.Callable[[Result, t.Optional[bytes]], None]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Reads data asynchronously from the game's allocated save file.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) and data (bytes) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result, data, data_length):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
if result == Result.ok:
|
||||||
|
data = bytes(data[:data_length])
|
||||||
|
callback(result, data)
|
||||||
|
else:
|
||||||
|
callback(result, None)
|
||||||
|
|
||||||
|
c_callback = self._internal.read_async.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
name = ctypes.c_char_p(name.encode("utf8"))
|
||||||
|
self._internal.read_async(self._internal, name, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def read_async_partial(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
offset: int,
|
||||||
|
length: int,
|
||||||
|
callback: t.Callable[[Result], None]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Reads data asynchronously from the game's allocated save file, starting at a given offset
|
||||||
|
and up to a given length.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result, data, data_length):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
if result == Result.ok:
|
||||||
|
data = bytes(data[:data_length])
|
||||||
|
callback(result, data)
|
||||||
|
else:
|
||||||
|
callback(result, None)
|
||||||
|
|
||||||
|
c_callback = self._internal.read_async.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
name = ctypes.c_char_p(name.encode("utf8"))
|
||||||
|
self._internal.read_async_partial(
|
||||||
|
self._internal,
|
||||||
|
name,
|
||||||
|
offset,
|
||||||
|
length,
|
||||||
|
ctypes.c_void_p(),
|
||||||
|
c_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
def write(self, name: str, data: bytes) -> None:
|
||||||
|
"""
|
||||||
|
Writes data synchronously to disk, under the given key name.
|
||||||
|
"""
|
||||||
|
name = ctypes.c_char_p(name.encode("utf8"))
|
||||||
|
data = (ctypes.c_uint8 * len(data))(*data)
|
||||||
|
|
||||||
|
result = Result(self._internal.write(self._internal, name, data, len(data)))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def write_async(self, name: str, data: bytes, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Writes data asynchronously to disk under the given keyname.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.write_async.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
name = ctypes.c_char_p(name.encode("utf8"))
|
||||||
|
data = (ctypes.c_uint8 * len(data))(*data)
|
||||||
|
|
||||||
|
self._internal.write_async(
|
||||||
|
self._internal,
|
||||||
|
name,
|
||||||
|
data,
|
||||||
|
len(data),
|
||||||
|
ctypes.c_void_p(),
|
||||||
|
c_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete(self, name: str) -> None:
|
||||||
|
"""
|
||||||
|
Deletes written data for the given key name.
|
||||||
|
"""
|
||||||
|
name = ctypes.c_char_p(name.encode("utf8"))
|
||||||
|
|
||||||
|
result = Result(self._internal.delete_(self._internal, name))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def exists(self, name: str) -> bool:
|
||||||
|
"""
|
||||||
|
Checks if data exists for a given key name.
|
||||||
|
"""
|
||||||
|
exists = ctypes.c_bool()
|
||||||
|
name = ctypes.c_char_p(name.encode("utf8"))
|
||||||
|
|
||||||
|
result = Result(self._internal.exists(self._internal, name, exists))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return exists.value
|
||||||
|
|
||||||
|
def stat(self, name: str) -> FileStat:
|
||||||
|
"""
|
||||||
|
Returns file info for the given key name.
|
||||||
|
"""
|
||||||
|
stat = sdk.DiscordFileStat()
|
||||||
|
|
||||||
|
name = ctypes.c_char_p(name.encode("utf8"))
|
||||||
|
result = Result(self._internal.stat(self._internal, name, stat))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return FileStat(internal=stat)
|
||||||
|
|
||||||
|
def count(self) -> int:
|
||||||
|
"""
|
||||||
|
Returns the count of files, for iteration.
|
||||||
|
"""
|
||||||
|
count = ctypes.c_int32()
|
||||||
|
self._internal.count(self._internal, count)
|
||||||
|
return count.value
|
||||||
|
|
||||||
|
def stat_at(self, index: int) -> FileStat:
|
||||||
|
"""
|
||||||
|
Returns file info for the given index when iterating over files.
|
||||||
|
"""
|
||||||
|
stat = sdk.DiscordFileStat()
|
||||||
|
|
||||||
|
result = Result(self._internal.stat_at(self._internal, index, stat))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return FileStat(internal=stat)
|
164
py_discord_sdk/discordsdk/store.py
Normal file
164
py_discord_sdk/discordsdk/store.py
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
import ctypes
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from . import sdk
|
||||||
|
from .enum import Result
|
||||||
|
from .event import bind_events
|
||||||
|
from .exception import get_exception
|
||||||
|
from .model import Entitlement, Sku
|
||||||
|
|
||||||
|
|
||||||
|
class StoreManager:
|
||||||
|
_internal: sdk.IDiscordStoreManager = None
|
||||||
|
_garbage: t.List[t.Any]
|
||||||
|
_events: sdk.IDiscordStoreEvents
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._garbage = []
|
||||||
|
self._events = bind_events(
|
||||||
|
sdk.IDiscordStoreEvents,
|
||||||
|
self._on_entitlement_create,
|
||||||
|
self._on_entitlement_delete
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_entitlement_create(self, event_data, entitlement):
|
||||||
|
self.on_entitlement_create(Entitlement(copy=entitlement))
|
||||||
|
|
||||||
|
def _on_entitlement_delete(self, event_data, entitlement):
|
||||||
|
self.on_entitlement_delete(Entitlement(copy=entitlement))
|
||||||
|
|
||||||
|
def fetch_skus(self, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Fetches the list of SKUs for the connected application, readying them for iteration.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.fetch_skus.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.fetch_skus(self._internal, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def count_skus(self) -> int:
|
||||||
|
"""
|
||||||
|
Get the number of SKUs readied by FetchSkus().
|
||||||
|
"""
|
||||||
|
count = ctypes.c_int32()
|
||||||
|
self._internal.count_skus(self._internal, count)
|
||||||
|
return count.value
|
||||||
|
|
||||||
|
def get_sku(self, sku_id: int) -> Sku:
|
||||||
|
"""
|
||||||
|
Gets a SKU by its ID.
|
||||||
|
"""
|
||||||
|
sku = sdk.DiscordSku()
|
||||||
|
|
||||||
|
result = Result(self._internal.get_sku(sku_id, sku))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return Sku(internal=sku)
|
||||||
|
|
||||||
|
def get_sku_at(self, index: int) -> Sku:
|
||||||
|
"""
|
||||||
|
Gets a SKU by index when iterating over SKUs.
|
||||||
|
"""
|
||||||
|
sku = sdk.DiscordSku()
|
||||||
|
|
||||||
|
result = Result(self._internal.get_sku_at(index, sku))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return Sku(internal=sku)
|
||||||
|
|
||||||
|
def fetch_entitlements(self, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Fetches a list of entitlements to which the user is entitled.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.fetch_entitlements.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.fetch_entitlements(self._internal, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def count_entitlements(self) -> int:
|
||||||
|
"""
|
||||||
|
Get the number of entitlements readied by FetchEntitlements().
|
||||||
|
"""
|
||||||
|
count = ctypes.c_int32()
|
||||||
|
self._internal.count_entitlements(self._internal, count)
|
||||||
|
return count.value
|
||||||
|
|
||||||
|
def get_entitlement(self, entitlement_id: int) -> Entitlement:
|
||||||
|
"""
|
||||||
|
Gets an entitlement by its id.
|
||||||
|
"""
|
||||||
|
entitlement = sdk.DiscordEntitlement()
|
||||||
|
|
||||||
|
result = Result(self._internal.get_entitlement(entitlement_id, entitlement))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return Entitlement(internal=Sku)
|
||||||
|
|
||||||
|
def get_entitlement_at(self, index: int) -> Entitlement:
|
||||||
|
"""
|
||||||
|
Gets an entitlement by index when iterating over a user's entitlements.
|
||||||
|
"""
|
||||||
|
entitlement = sdk.DiscordEntitlement()
|
||||||
|
|
||||||
|
result = Result(self._internal.get_entitlement_at(index, entitlement))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return Entitlement(internal=Sku)
|
||||||
|
|
||||||
|
def has_sku_entitlement(self, sku_id: int) -> bool:
|
||||||
|
"""
|
||||||
|
Returns whether or not the user is entitled to the given SKU ID.
|
||||||
|
"""
|
||||||
|
has_entitlement = ctypes.c_bool()
|
||||||
|
|
||||||
|
result = Result(self._internal.has_sku_entitlement(sku_id, has_entitlement))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return has_entitlement.value
|
||||||
|
|
||||||
|
def start_purchase(self, sku_id: int, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Opens the overlay to begin the in-app purchase dialogue for the given SKU ID.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.start_purchase.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.start_purchase(self._internal, sku_id, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def on_entitlement_create(self, entitlement: Entitlement) -> None:
|
||||||
|
"""
|
||||||
|
Fires when the connected user receives a new entitlement, either through purchase or
|
||||||
|
through a developer grant.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_entitlement_delete(self, entitlement: Entitlement) -> None:
|
||||||
|
"""
|
||||||
|
Fires when the connected user loses an entitlement, either by expiration, revocation, or
|
||||||
|
consumption in the case of consumable entitlements.
|
||||||
|
"""
|
81
py_discord_sdk/discordsdk/user.py
Normal file
81
py_discord_sdk/discordsdk/user.py
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
import ctypes
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from . import sdk
|
||||||
|
from .enum import PremiumType, Result, UserFlag
|
||||||
|
from .event import bind_events
|
||||||
|
from .exception import get_exception
|
||||||
|
from .model import User
|
||||||
|
|
||||||
|
|
||||||
|
class UserManager:
|
||||||
|
_internal: sdk.IDiscordUserManager = None
|
||||||
|
_garbage: t.List[t.Any]
|
||||||
|
_events: sdk.IDiscordUserEvents
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._garbage = []
|
||||||
|
self._events = bind_events(
|
||||||
|
sdk.IDiscordUserEvents,
|
||||||
|
self._on_current_user_update
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_current_user_update(self, event_data):
|
||||||
|
self.on_current_user_update()
|
||||||
|
|
||||||
|
def get_current_user(self) -> User:
|
||||||
|
"""
|
||||||
|
Fetch information about the currently connected user account.
|
||||||
|
"""
|
||||||
|
user = sdk.DiscordUser()
|
||||||
|
result = Result(self._internal.get_current_user(self._internal, user))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return User(internal=user)
|
||||||
|
|
||||||
|
def get_user(self, user_id: int, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Get user information for a given id.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) and User via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result, user):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
if result == Result.ok:
|
||||||
|
callback(result, User(copy=user.contents))
|
||||||
|
else:
|
||||||
|
callback(result, None)
|
||||||
|
|
||||||
|
c_callback = self._internal.get_user.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.get_user(self._internal, user_id, ctypes.c_void_p(), c_callback)
|
||||||
|
|
||||||
|
def get_current_user_premium_type(self) -> PremiumType:
|
||||||
|
"""
|
||||||
|
Get the PremiumType for the currently connected user.
|
||||||
|
"""
|
||||||
|
premium_type = ctypes.c_int32()
|
||||||
|
result = Result(self._internal.get_current_user_premium_type(self._internal, premium_type))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return PremiumType(premium_type.value)
|
||||||
|
|
||||||
|
def current_user_has_flag(self, flag: UserFlag) -> bool:
|
||||||
|
"""
|
||||||
|
See whether or not the current user has a certain UserFlag on their account.
|
||||||
|
"""
|
||||||
|
has_flag = ctypes.c_bool()
|
||||||
|
result = Result(self._internal.current_user_has_flag(self._internal, flag, has_flag))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return has_flag.value
|
||||||
|
|
||||||
|
def on_current_user_update(self) -> None:
|
||||||
|
"""
|
||||||
|
Fires when the User struct of the currently connected user changes.
|
||||||
|
"""
|
136
py_discord_sdk/discordsdk/voice.py
Normal file
136
py_discord_sdk/discordsdk/voice.py
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
import ctypes
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from . import sdk
|
||||||
|
from .enum import Result
|
||||||
|
from .event import bind_events
|
||||||
|
from .exception import get_exception
|
||||||
|
from .model import InputMode
|
||||||
|
|
||||||
|
|
||||||
|
class VoiceManager:
|
||||||
|
_internal: sdk.IDiscordVoiceManager = None
|
||||||
|
_garbage: t.List[t.Any]
|
||||||
|
_events: sdk.IDiscordVoiceEvents
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._garbage = []
|
||||||
|
self._events = bind_events(
|
||||||
|
sdk.IDiscordVoiceEvents,
|
||||||
|
self._on_settings_update
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_settings_update(self, event_data):
|
||||||
|
self.on_settings_update()
|
||||||
|
|
||||||
|
def get_input_mode(self) -> InputMode:
|
||||||
|
"""
|
||||||
|
Get the current voice input mode for the user
|
||||||
|
"""
|
||||||
|
input_mode = sdk.DiscordInputMode()
|
||||||
|
result = Result(self._internal.get_input_mode(self._internal, input_mode))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return InputMode(internal=input_mode)
|
||||||
|
|
||||||
|
def set_input_mode(self, inputMode: InputMode, callback: t.Callable[[Result], None]) -> None:
|
||||||
|
"""
|
||||||
|
Sets a new voice input mode for the uesr.
|
||||||
|
|
||||||
|
Returns discordsdk.enum.Result (int) via callback.
|
||||||
|
"""
|
||||||
|
def c_callback(callback_data, result):
|
||||||
|
self._garbage.remove(c_callback)
|
||||||
|
result = Result(result)
|
||||||
|
callback(result)
|
||||||
|
|
||||||
|
c_callback = self._internal.set_input_mode.argtypes[-1](c_callback)
|
||||||
|
self._garbage.append(c_callback) # prevent it from being garbage collected
|
||||||
|
|
||||||
|
self._internal.set_input_mode(
|
||||||
|
self._internal,
|
||||||
|
inputMode._internal,
|
||||||
|
ctypes.c_void_p(),
|
||||||
|
c_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_self_mute(self) -> bool:
|
||||||
|
"""
|
||||||
|
Whether the connected user is currently muted.
|
||||||
|
"""
|
||||||
|
mute = ctypes.c_bool()
|
||||||
|
result = Result(self._internal.is_self_mute(self._internal, mute))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return mute.value
|
||||||
|
|
||||||
|
def set_self_mute(self, mute: bool) -> None:
|
||||||
|
"""
|
||||||
|
Mutes or unmutes the currently connected user.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.set_self_mute(self._internal, mute))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def is_self_deaf(self) -> bool:
|
||||||
|
"""
|
||||||
|
Whether the connected user is currently deafened.
|
||||||
|
"""
|
||||||
|
deaf = ctypes.c_bool()
|
||||||
|
result = Result(self._internal.is_self_deaf(self._internal, deaf))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return deaf.value
|
||||||
|
|
||||||
|
def set_self_deaf(self, deaf: bool) -> None:
|
||||||
|
"""
|
||||||
|
Deafens or undefeans the currently connected user.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.set_self_deaf(self._internal, deaf))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def is_local_mute(self, user_id: int) -> bool:
|
||||||
|
"""
|
||||||
|
Whether the given user is currently muted by the connected user.
|
||||||
|
"""
|
||||||
|
mute = ctypes.c_bool()
|
||||||
|
result = Result(self._internal.is_local_mute(self._internal, user_id, mute))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return mute.value
|
||||||
|
|
||||||
|
def set_local_mute(self, user_id: int, mute: bool) -> None:
|
||||||
|
"""
|
||||||
|
Mutes or unmutes the given user for the currently connected user.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.set_local_mute(self._internal, user_id, mute))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def get_local_volume(self, user_id: int) -> int:
|
||||||
|
"""
|
||||||
|
Gets the local volume for a given user.
|
||||||
|
"""
|
||||||
|
volume = ctypes.c_uint8()
|
||||||
|
result = Result(self._internal.get_local_volume(self._internal, user_id, volume))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
return volume.value
|
||||||
|
|
||||||
|
def set_local_volume(self, user_id: int, volume: int) -> None:
|
||||||
|
"""
|
||||||
|
Sets the local volume for a given user.
|
||||||
|
"""
|
||||||
|
result = Result(self._internal.set_local_volume(self._internal, user_id, volume))
|
||||||
|
if result != Result.ok:
|
||||||
|
raise get_exception(result)
|
||||||
|
|
||||||
|
def on_settings_update(self) -> None:
|
||||||
|
# This event is not documented anywhere (yet?)
|
||||||
|
pass
|
23
py_discord_sdk/setup.py
Normal file
23
py_discord_sdk/setup.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import setuptools
|
||||||
|
|
||||||
|
with open("README.md", "r") as fh:
|
||||||
|
long_description = fh.read()
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
name="discordsdk",
|
||||||
|
version="0.3dev",
|
||||||
|
author="LennyPhoenix & NathaanTFM",
|
||||||
|
author_email="lennyphoenixc@gmail.com",
|
||||||
|
description="Python wrapper around Discord's Game SDK library.",
|
||||||
|
license="LICENSE",
|
||||||
|
long_description=long_description,
|
||||||
|
long_description_content_type="text/markdown",
|
||||||
|
url="https://github.com/LennyPhoenix/py-discord-sdk",
|
||||||
|
packages=setuptools.find_packages(),
|
||||||
|
classifiers=[
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"License :: OSI Approved :: MIT License",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
],
|
||||||
|
python_requires=">= 3.5",
|
||||||
|
)
|
Loading…
Reference in a new issue