├── .gitignore ├── BUILD.md ├── LICENSE ├── README.md ├── RPC.ico ├── THIRD-PARTY-LICENSES.txt ├── Tools └── i18n │ ├── msgfmt.py │ └── pygettext.py ├── config.ini ├── dist └── installer.nsi ├── gw2rpc ├── api.py ├── character.py ├── gw2rpc.py ├── lib │ └── discordsdk │ │ ├── __init__.py │ │ ├── achievement.py │ │ ├── activity.py │ │ ├── application.py │ │ ├── discord.py │ │ ├── enum.py │ │ ├── event.py │ │ ├── exception.py │ │ ├── image.py │ │ ├── lobby.py │ │ ├── model.py │ │ ├── network.py │ │ ├── overlay.py │ │ ├── relationship.py │ │ ├── sdk.py │ │ ├── storage.py │ │ ├── store.py │ │ ├── user.py │ │ └── voice.py ├── mumble.py ├── rpc.py ├── sdk.py └── settings.py ├── icon.ico ├── launch_gw2_with_rpc.py ├── lib └── discord_game_sdk.dll ├── locales ├── base.pot ├── de │ └── LC_MESSAGES │ │ └── base.po ├── en │ └── LC_MESSAGES │ │ └── base.po ├── es │ └── LC_MESSAGES │ │ └── base.po ├── fr │ └── LC_MESSAGES │ │ └── base.po └── pt-Br │ └── LC_MESSAGES │ └── base.po ├── requirements.txt └── run.py /.gitignore: -------------------------------------------------------------------------------- 1 | test.py 2 | changelog.txt 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # dotenv 86 | .env 87 | 88 | # virtualenv 89 | .venv 90 | venv/ 91 | ENV/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | 107 | # VSC 108 | .vscode/ 109 | -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 |

・Build and development Instructions

2 | 3 | This project was tested with python3 version 3.9.1, allthough later versions might work too. 4 | 5 |

・Install dependencies

6 | 7 | Install dependencies with the command `pip3 install -r requirements.txt` 8 | 9 |

・Generating locales

10 | 11 | First you have to generate the binary locale files with `msgformat.py`, i.e. 12 | ``` 13 | cd locales/de/LC_MESSAGES/ 14 | ../../../Tools/i18n/msgfmt.py -o base.mo base 15 | ``` 16 | to do this for all available languages in one command, run the following from the project root directory: 17 | ``` 18 | for file in $(ls locales); do cd locales/${file}/LC_MESSAGES && ../../../Tools/i18n/msgfmt.py -o base.mo base && cd ../../../; done 19 | ``` 20 | Inside the LC_MESSAGES folders, you should now have the `base.po` and `base.mo` files. 21 | 22 |

・Running

23 | 24 | Make sure to change `locales_path = resource_path("./locales")` to `locales_path = resource_path("../locales")` in `gw2rpc.py` and run the program from the projects root directory with `python.exe .\run.py`. 25 | 26 | Make sure that you run it from a Windows Terminal / Powershell as there are some Windows specific dependencies to get the tasks list. The tray icon should appear when the program is running. 27 | 28 |

・Debugging

29 | 30 | Something like 31 | ``` 32 | print("{} {}".format(map_id, map_name)) 33 | print("{} {}".format(position.x, position.y)) 34 | ``` 35 | in the `get_map_asset` function in `gw2rpc.py` might be helpful to develop and debug. 36 | 37 |

・Build

38 | 39 | First make sure to change the `locales_path` back to `./locales`, as mentioned above. 40 | 41 | Next make sure to set the path for the discord lib in `lib/discordsdk/sdk.py`, line `20`: 42 | 43 | From 44 | ```py 45 | dll = ctypes.CDLL(os.path.abspath("lib/discord_game_sdk")) 46 | ``` 47 | to 48 | ```py 49 | dll = ctypes.CDLL('discord_game_sdk.dll') 50 | ``` 51 | 52 | Next, create a .spec file for the project and place it in the projects root directory. It might look like the following: 53 | ``` 54 | # -*- mode: python ; coding: utf-8 -*- 55 | 56 | block_cipher = None 57 | 58 | added_files = [("locales", "locales"), ('icon.ico', '.'), ('RPC.ico', '.'), ('lib\\discord_game_sdk.dll', '.')] 59 | 60 | a = Analysis(['run.py'], 61 | pathex=['C:\\Users\\X\\Projects\\GW2RPC'], 62 | binaries=[], 63 | datas=added_files, 64 | hiddenimports=['pkg_resources', 'infi.systray'], 65 | hookspath=[], 66 | runtime_hooks=[], 67 | excludes=[], 68 | win_no_prefer_redirects=False, 69 | win_private_assemblies=False, 70 | cipher=block_cipher, 71 | noarchive=False) 72 | pyz = PYZ(a.pure, a.zipped_data, 73 | cipher=block_cipher) 74 | exe = EXE(pyz, 75 | a.scripts, 76 | a.binaries, 77 | a.zipfiles, 78 | a.datas, 79 | [], 80 | name='gw2rpc.exe', 81 | debug=False, 82 | bootloader_ignore_signals=False, 83 | strip=False, 84 | upx=True, 85 | upx_exclude=[], 86 | runtime_tmpdir=None, 87 | console=False , icon='RPC.ico') 88 | ``` 89 | Adapt the path to your project in the .spec file accordingly. 90 | 91 | Start compiling simply with `pyinstaller.exe .\run.spec`. Pyinstaller 4.3 was used for this. 92 | 93 | Again make sure that you run pyinstaller from your Windows installation! 94 | 95 | If you get any errors when starting the compiled exe (like `Failed to execute script`), change `console` to `True` in the .spec file, run pyinstaller again and start the compiled exe from a powershell window. You hopefully will get error messages then. 96 | 97 |

Installer

98 | 99 | The installer is created with NSIS and the nsis script located in dist folder 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Guild Wars 2 RPC

2 | 3 |

Guild Wars 2 RPC

4 |

A Discord Rich Presence addon for Guild Wars 2.

5 | 6 |

7 | RPC status example 8 |

9 | 10 |

11 | Go to websiteDownload LatestOther ProjectsDonate

12 | 13 | --- 14 |

You need to install the Discord Desktop version to run GW2RPC.

15 | 16 |

・Features

17 |
Displaying off 18 | 19 | * current map, as well as closest point of interest 20 | * current raid, strike or fractal boss 21 | * character name, race and profession (elite spec) 22 | * commander icon if player is currently commanding a squad 23 | * active guild tag (needs API key) 24 | * time spent on map 25 | 26 |
27 | 28 |
Also 29 | 30 | * Automatic update checking 31 | * Web based registry for maps 32 | * Configurable settings 33 | * Supports multiple accounts and multiboxing 34 | * Automatic raid announcer (FAQ): 35 | 36 |

37 | RPC commander example 38 |

39 | 40 |
41 | 42 |
43 |

・ How to install

44 | 45 | 1. Use the installer. Start either `gw2rpc.exe` or `launch_gw2_with_rpc.exe`. 46 | 47 | or 48 | 49 | 2. Extract `gw2rpc.zip` and run the `gw2rpc.exe`. It will start in your system tray. It needs to be running in background for Rich Presence to work. 50 | 51 | In the config.ini in the program's directory, you can input your API key so that your status can display region (EU/NA) and your current guild tag. 52 | 53 | To make starting Rich Presence easier, there is an .exe called `launch_gw2_with_rpc.exe` included in the download. This script launches both GW2 and the RPC addon. For it to work, it needs to be present in `GW2FOLDER\addons\gw2rpc` . You may then replace your normal GW2 shortcut with it for ease of launching. 54 | 55 | You can also put a shortcut to `gw2rpc.exe` into your autorun so that it runs automatically on Windows boot. 56 | 57 |
58 |

・ How to update

59 | 60 | 1. Run the new installer again. 61 | 62 | or 63 | 64 | 65 | 2. If a new version is released, simply replace the updated files. To get the newest configuration file, you might also delete the old one and let it be recreated on the first start of `gw2rpc.exe` for you. 66 | 67 |
68 |

・Configuration file

69 | 70 | See below for the example configuration file 71 | ```md 72 | 73 | [API] 74 | APIkey = ; ABCDE-FFFF-12345-.... 75 | 76 | [Settings] 77 | CloseWithGW2 = False ; Exit gw2rpc if GW2 exits 78 | DisplayGuildTag = True 79 | HideCommanderTag = False ; Dont show active comm tag if True 80 | Lang = en ; Localization, one of en, es, fr, de, pt-br 81 | LogLevel = info ; One of debug, info, warning, critical 82 | 83 | [PointsOfInterest] 84 | DisableInWvW = False 85 | DisableCompletely = False 86 | HidePoiButton = False ; Dont show the copy paste button for PoI if true 87 | 88 | [Webhooks] 89 | webhook = https://discord.com/api/webhooks/887....b3bfdyCiz7y 90 | AnnounceRaid = True 91 | DisableInWvW = False 92 | ``` 93 |
94 | 95 |

・Build and development Instructions

96 | See build section for instructions 97 | 98 |
99 |

・Donate

100 | This project is done in our free time by volunteers. If you want to support us, a donation is highly appreciated! 101 | 102 |
103 | 104 | [![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UEBHM63V547KN) -------------------------------------------------------------------------------- /RPC.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maselkov/GW2RPC/444d25c36db200aca1955f284e6a502c4e445c49/RPC.ico -------------------------------------------------------------------------------- /THIRD-PARTY-LICENSES.txt: -------------------------------------------------------------------------------- 1 | psutil is distributed under BSD license reproduced below. 2 | 3 | Copyright (c) 2009, Jay Loden, Dave Daeschler, Giampaolo Rodola' 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | * Neither the name of the psutil authors nor the names of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | 30 | 31 | 32 | Copyright (c) 2017 INFINIDAT 33 | 34 | Redistribution and use in source and binary forms, with or without 35 | modification, are permitted provided that the following conditions are met: 36 | 37 | 1. Redistributions of source code must retain the above copyright notice, this 38 | list of conditions and the following disclaimer. 39 | 40 | 2. Redistributions in binary form must reproduce the above copyright notice, 41 | this list of conditions and the following disclaimer in the documentation 42 | and/or other materials provided with the distribution. 43 | 44 | 3. Neither the name of the copyright holder nor the names of its contributors 45 | may be used to endorse or promote products derived from this software 46 | without specific prior written permission. 47 | 48 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 49 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 50 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 51 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 52 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 54 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 55 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 56 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 57 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 58 | 59 | 60 | 61 | Copyright 2017 Kenneth Reitz 62 | 63 | Licensed under the Apache License, Version 2.0 (the "License"); 64 | you may not use this file except in compliance with the License. 65 | You may obtain a copy of the License at 66 | 67 | http://www.apache.org/licenses/LICENSE-2.0 68 | 69 | Unless required by applicable law or agreed to in writing, software 70 | distributed under the License is distributed on an "AS IS" BASIS, 71 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 72 | See the License for the specific language governing permissions and 73 | limitations under the License. 74 | 75 | 76 | MIT License 77 | 78 | Copyright (c) 2020 NathaanTFM 79 | Copyright (c) 2020 LennyPhoenix 80 | 81 | Permission is hereby granted, free of charge, to any person obtaining a copy 82 | of this software and associated documentation files (the "Software"), to deal 83 | in the Software without restriction, including without limitation the rights 84 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 85 | copies of the Software, and to permit persons to whom the Software is 86 | furnished to do so, subject to the following conditions: 87 | 88 | The above copyright notice and this permission notice shall be included in all 89 | copies or substantial portions of the Software. 90 | 91 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 92 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 93 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 94 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 95 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 96 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 97 | SOFTWARE. -------------------------------------------------------------------------------- /Tools/i18n/msgfmt.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # Written by Martin v. Löwis 3 | 4 | """Generate binary message catalog from textual translation description. 5 | 6 | This program converts a textual Uniforum-style message catalog (.po file) into 7 | a binary GNU catalog (.mo file). This is essentially the same function as the 8 | GNU msgfmt program, however, it is a simpler implementation. Currently it 9 | does not handle plural forms but it does handle message contexts. 10 | 11 | Usage: msgfmt.py [OPTIONS] filename.po 12 | 13 | Options: 14 | -o file 15 | --output-file=file 16 | Specify the output file to write to. If omitted, output will go to a 17 | file named filename.mo (based off the input file name). 18 | 19 | -h 20 | --help 21 | Print this message and exit. 22 | 23 | -V 24 | --version 25 | Display version information and exit. 26 | """ 27 | 28 | import os 29 | import sys 30 | import ast 31 | import getopt 32 | import struct 33 | import array 34 | from email.parser import HeaderParser 35 | 36 | __version__ = "1.2" 37 | 38 | MESSAGES = {} 39 | 40 | 41 | def usage(code, msg=''): 42 | print(__doc__, file=sys.stderr) 43 | if msg: 44 | print(msg, file=sys.stderr) 45 | sys.exit(code) 46 | 47 | 48 | def add(ctxt, id, str, fuzzy): 49 | "Add a non-fuzzy translation to the dictionary." 50 | global MESSAGES 51 | if not fuzzy and str: 52 | if ctxt is None: 53 | MESSAGES[id] = str 54 | else: 55 | MESSAGES[b"%b\x04%b" % (ctxt, id)] = str 56 | 57 | 58 | def generate(): 59 | "Return the generated output." 60 | global MESSAGES 61 | # the keys are sorted in the .mo file 62 | keys = sorted(MESSAGES.keys()) 63 | offsets = [] 64 | ids = strs = b'' 65 | for id in keys: 66 | # For each string, we need size and file offset. Each string is NUL 67 | # terminated; the NUL does not count into the size. 68 | offsets.append((len(ids), len(id), len(strs), len(MESSAGES[id]))) 69 | ids += id + b'\0' 70 | strs += MESSAGES[id] + b'\0' 71 | output = '' 72 | # The header is 7 32-bit unsigned integers. We don't use hash tables, so 73 | # the keys start right after the index tables. 74 | # translated string. 75 | keystart = 7*4+16*len(keys) 76 | # and the values start after the keys 77 | valuestart = keystart + len(ids) 78 | koffsets = [] 79 | voffsets = [] 80 | # The string table first has the list of keys, then the list of values. 81 | # Each entry has first the size of the string, then the file offset. 82 | for o1, l1, o2, l2 in offsets: 83 | koffsets += [l1, o1+keystart] 84 | voffsets += [l2, o2+valuestart] 85 | offsets = koffsets + voffsets 86 | output = struct.pack("Iiiiiii", 87 | 0x950412de, # Magic 88 | 0, # Version 89 | len(keys), # # of entries 90 | 7*4, # start of key index 91 | 7*4+len(keys)*8, # start of value index 92 | 0, 0) # size and offset of hash table 93 | output += array.array("i", offsets).tobytes() 94 | output += ids 95 | output += strs 96 | return output 97 | 98 | 99 | def make(filename, outfile): 100 | ID = 1 101 | STR = 2 102 | CTXT = 3 103 | 104 | # Compute .mo name from .po name and arguments 105 | if filename.endswith('.po'): 106 | infile = filename 107 | else: 108 | infile = filename + '.po' 109 | if outfile is None: 110 | outfile = os.path.splitext(infile)[0] + '.mo' 111 | 112 | try: 113 | with open(infile, 'rb') as f: 114 | lines = f.readlines() 115 | except IOError as msg: 116 | print(msg, file=sys.stderr) 117 | sys.exit(1) 118 | 119 | section = msgctxt = None 120 | fuzzy = 0 121 | 122 | # Start off assuming Latin-1, so everything decodes without failure, 123 | # until we know the exact encoding 124 | encoding = 'latin-1' 125 | 126 | # Parse the catalog 127 | lno = 0 128 | for l in lines: 129 | l = l.decode(encoding) 130 | lno += 1 131 | # If we get a comment line after a msgstr, this is a new entry 132 | if l[0] == '#' and section == STR: 133 | add(msgctxt, msgid, msgstr, fuzzy) 134 | section = msgctxt = None 135 | fuzzy = 0 136 | # Record a fuzzy mark 137 | if l[:2] == '#,' and 'fuzzy' in l: 138 | fuzzy = 1 139 | # Skip comments 140 | if l[0] == '#': 141 | continue 142 | # Now we are in a msgid or msgctxt section, output previous section 143 | if l.startswith('msgctxt'): 144 | if section == STR: 145 | add(msgctxt, msgid, msgstr, fuzzy) 146 | section = CTXT 147 | l = l[7:] 148 | msgctxt = b'' 149 | elif l.startswith('msgid') and not l.startswith('msgid_plural'): 150 | if section == STR: 151 | add(msgctxt, msgid, msgstr, fuzzy) 152 | if not msgid: 153 | # See whether there is an encoding declaration 154 | p = HeaderParser() 155 | charset = p.parsestr(msgstr.decode(encoding)).get_content_charset() 156 | if charset: 157 | encoding = charset 158 | section = ID 159 | l = l[5:] 160 | msgid = msgstr = b'' 161 | is_plural = False 162 | # This is a message with plural forms 163 | elif l.startswith('msgid_plural'): 164 | if section != ID: 165 | print('msgid_plural not preceded by msgid on %s:%d' % (infile, lno), 166 | file=sys.stderr) 167 | sys.exit(1) 168 | l = l[12:] 169 | msgid += b'\0' # separator of singular and plural 170 | is_plural = True 171 | # Now we are in a msgstr section 172 | elif l.startswith('msgstr'): 173 | section = STR 174 | if l.startswith('msgstr['): 175 | if not is_plural: 176 | print('plural without msgid_plural on %s:%d' % (infile, lno), 177 | file=sys.stderr) 178 | sys.exit(1) 179 | l = l.split(']', 1)[1] 180 | if msgstr: 181 | msgstr += b'\0' # Separator of the various plural forms 182 | else: 183 | if is_plural: 184 | print('indexed msgstr required for plural on %s:%d' % (infile, lno), 185 | file=sys.stderr) 186 | sys.exit(1) 187 | l = l[6:] 188 | # Skip empty lines 189 | l = l.strip() 190 | if not l: 191 | continue 192 | l = ast.literal_eval(l) 193 | if section == CTXT: 194 | msgctxt += l.encode(encoding) 195 | elif section == ID: 196 | msgid += l.encode(encoding) 197 | elif section == STR: 198 | msgstr += l.encode(encoding) 199 | else: 200 | print('Syntax error on %s:%d' % (infile, lno), \ 201 | 'before:', file=sys.stderr) 202 | print(l, file=sys.stderr) 203 | sys.exit(1) 204 | # Add last entry 205 | if section == STR: 206 | add(msgctxt, msgid, msgstr, fuzzy) 207 | 208 | # Compute output 209 | output = generate() 210 | 211 | try: 212 | with open(outfile,"wb") as f: 213 | f.write(output) 214 | except IOError as msg: 215 | print(msg, file=sys.stderr) 216 | 217 | 218 | def main(): 219 | try: 220 | opts, args = getopt.getopt(sys.argv[1:], 'hVo:', 221 | ['help', 'version', 'output-file=']) 222 | except getopt.error as msg: 223 | usage(1, msg) 224 | 225 | outfile = None 226 | # parse options 227 | for opt, arg in opts: 228 | if opt in ('-h', '--help'): 229 | usage(0) 230 | elif opt in ('-V', '--version'): 231 | print("msgfmt.py", __version__) 232 | sys.exit(0) 233 | elif opt in ('-o', '--output-file'): 234 | outfile = arg 235 | # do it 236 | if not args: 237 | print('No input file given', file=sys.stderr) 238 | print("Try `msgfmt --help' for more information.", file=sys.stderr) 239 | return 240 | 241 | for filename in args: 242 | make(filename, outfile) 243 | 244 | 245 | if __name__ == '__main__': 246 | main() 247 | -------------------------------------------------------------------------------- /Tools/i18n/pygettext.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maselkov/GW2RPC/444d25c36db200aca1955f284e6a502c4e445c49/Tools/i18n/pygettext.py -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [API] 2 | APIkey = 3 | 4 | [Settings] 5 | CloseWithGW2 = False 6 | DisplayGuildTag = True 7 | HideCommanderTag = False 8 | HideMounts = False 9 | Lang = en 10 | LogLevel = info 11 | 12 | [PointsOfInterest] 13 | DisableInWvW = False 14 | DisableCompletely = False 15 | HidePoiButton = False 16 | 17 | [Webhooks] 18 | webhook = 19 | AnnounceRaid = True 20 | DisableInWvW = False 21 | -------------------------------------------------------------------------------- /dist/installer.nsi: -------------------------------------------------------------------------------- 1 | ;-------------------------------- 2 | !include MUI2.nsh 3 | !define MUI_INSTFILESPAGE_COLORS "FFFFFF 000000" ;Two colors 4 | !define MUI_ICON "..\icon.ico" 5 | 6 | ; The name of the installer 7 | Name "GW2RPC" 8 | 9 | ; The file to write 10 | OutFile "gw2rpc_installer.exe" 11 | 12 | ; Request application privileges for Windows Vista and higher 13 | RequestExecutionLevel admin 14 | 15 | ; Build Unicode installer 16 | Unicode True 17 | 18 | 19 | ; Registry key to check for directory (so if you install again, it will 20 | ; overwrite the old one automatically) 21 | InstallDirRegKey HKLM "Software\GW2RPC" "Install_Dir" 22 | 23 | ;-------------------------------- 24 | 25 | ; Pages 26 | 27 | 28 | !define MUI_PAGE_DIRECTORY_TEXT "Please select your Guild Wars 2 installation folder. To install in a different folder, click Browse and select another folder. GW2RPC needs to be installed inside 'Guild Wars 2\addons'. Click Install to start the installation." 29 | 30 | 31 | !define MUI_FINISHPAGE_TEXT "GW2RPC installed successfully! You may use Launch_GW2_with_RPC.exe to start GW2 together with GW2RPC or start gw2rpc.exe by yourself." 32 | !define MUI_FINISHPAGE_TITLE "GW2RPC" 33 | !define MUI_FINISHPAGE_LINK "Visit the GW2RPC website for more information" 34 | !define MUI_FINISHPAGE_LINK_LOCATION "https://gw2rpc.info" 35 | !define MUI_FINISHPAGE_RUN "$INSTDIR\Launch_GW2_with_RPC.exe" 36 | !define MUI_FINISHPAGE_RUN_TEXT 'Launch GW2 together with GW2RPC' 37 | !define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\config.ini" 38 | !define MUI_FINISHPAGE_SHOWREADME_TEXT "Open config file" 39 | !define MUI_FINISHPAGE_NOREBOOTSUPPORT 40 | 41 | !insertmacro MUI_PAGE_LICENSE "..\LICENSE" 42 | !insertmacro MUI_PAGE_COMPONENTS 43 | !insertmacro MUI_PAGE_DIRECTORY 44 | !insertmacro MUI_PAGE_INSTFILES 45 | !insertmacro MUI_PAGE_FINISH 46 | 47 | UninstPage uninstConfirm 48 | UninstPage instfiles 49 | 50 | !define StrRep "!insertmacro StrRep" 51 | !macro StrRep output string old new 52 | Push `${string}` 53 | Push `${old}` 54 | Push `${new}` 55 | !ifdef __UNINSTALL__ 56 | Call un.StrRep 57 | !else 58 | Call StrRep 59 | !endif 60 | Pop ${output} 61 | !macroend 62 | 63 | !macro Func_StrRep un 64 | Function ${un}StrRep 65 | Exch $R2 ;new 66 | Exch 1 67 | Exch $R1 ;old 68 | Exch 2 69 | Exch $R0 ;string 70 | Push $R3 71 | Push $R4 72 | Push $R5 73 | Push $R6 74 | Push $R7 75 | Push $R8 76 | Push $R9 77 | StrCpy $R3 0 78 | StrLen $R4 $R1 79 | StrLen $R6 $R0 80 | StrLen $R9 $R2 81 | loop: 82 | StrCpy $R5 $R0 $R4 $R3 83 | StrCmp $R5 $R1 found 84 | StrCmp $R3 $R6 done 85 | IntOp $R3 $R3 + 1 ;move offset by 1 to check the next character 86 | Goto loop 87 | found: 88 | StrCpy $R5 $R0 $R3 89 | IntOp $R8 $R3 + $R4 90 | StrCpy $R7 $R0 "" $R8 91 | StrCpy $R0 $R5$R2$R7 92 | StrLen $R6 $R0 93 | IntOp $R3 $R3 + $R9 ;move offset by length of the replacement string 94 | Goto loop 95 | done: 96 | Pop $R9 97 | Pop $R8 98 | Pop $R7 99 | Pop $R6 100 | Pop $R5 101 | Pop $R4 102 | Pop $R3 103 | Push $R0 104 | Push $R1 105 | Pop $R0 106 | Pop $R1 107 | Pop $R0 108 | Pop $R2 109 | Exch $R1 110 | FunctionEnd 111 | !macroend 112 | !insertmacro Func_StrRep "" 113 | !insertmacro Func_StrRep "un." 114 | 115 | Function .onInit 116 | ; The default installation directory of GW2 117 | SetRegView 64 118 | ReadRegStr $1 HKLM "Software\ArenaNet\Guild Wars 2" "Path" 119 | DetailPrint "$1" 120 | SetRegView lastused 121 | 122 | ; Check if the installation path was found 123 | StrCmp $1 "" install_path_not_found 124 | ; Replace Gw2-64.exe with addons in the installation path 125 | ${StrRep} $2 $1 "Gw2-64.exe" "addons\GW2RPC" 126 | StrCmp $2 $1 install_path_not_found 127 | 128 | StrCpy $INSTDIR $2 129 | ;SetOutPath $2 130 | Goto done 131 | 132 | install_path_not_found: 133 | StrCpy $INSTDIR "C:\Program Files\Guild Wars 2\addons\GW2RPC\" 134 | ;SetOutPath $INSTDIR 135 | done: 136 | 137 | FunctionEnd 138 | 139 | ;-------------------------------- 140 | 141 | ; The stuff to install 142 | Section "GW2RPC (required)" 143 | 144 | SetOutPath $INSTDIR 145 | SectionIn RO 146 | 147 | ; Put file there 148 | File "gw2rpc.exe" 149 | IfFileExists $INSTDIR\config.ini Skip 150 | File "config.ini" 151 | Skip: 152 | File "Launch_GW2_with_RPC.exe" 153 | 154 | ; Write the installation path into the registry 155 | WriteRegStr HKLM SOFTWARE\GW2RPC "Install_Dir" "$INSTDIR" 156 | 157 | ; Write the uninstall keys for Windows 158 | WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\GW2RPC" "DisplayName" "GW2RPC" 159 | WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\GW2RPC" "UninstallString" '"$INSTDIR\uninstall.exe"' 160 | WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\GW2RPC" "NoModify" 1 161 | WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\GW2RPC" "NoRepair" 1 162 | WriteUninstaller "$INSTDIR\uninstall.exe" 163 | 164 | SectionEnd 165 | 166 | ; Optional section (can be disabled by the user) 167 | Section "Start Menu Shortcuts" 168 | 169 | CreateDirectory "$SMPROGRAMS\GW2RPC" 170 | CreateShortcut "$SMPROGRAMS\GW2RPC\Uninstall.lnk" "$INSTDIR\uninstall.exe" 171 | CreateShortcut "$SMPROGRAMS\GW2RPC\gw2rpc.lnk" "$INSTDIR\gw2rpc.exe" 172 | CreateShortcut "$SMPROGRAMS\GW2RPC\Launch_GW2_with_RPC.lnk" "$INSTDIR\Launch_GW2_with_RPC.exe" 173 | 174 | SectionEnd 175 | 176 | Section "Desktop Shortcuts" 177 | 178 | CreateShortcut "$desktop\gw2rpc.lnk" "$INSTDIR\gw2rpc.exe" 179 | CreateShortcut "$desktop\Launch_GW2_with_RPC.lnk" "$INSTDIR\Launch_GW2_with_RPC.exe" 180 | 181 | SectionEnd 182 | 183 | Section /o "Autorun on Windows Startup" 184 | 185 | CreateShortcut "$SMPROGRAMS\Startup\gw2rpc.lnk" "$INSTDIR\gw2rpc.exe" 186 | 187 | SectionEnd 188 | 189 | ;-------------------------------- 190 | 191 | ; Uninstaller 192 | 193 | Section "Uninstall" 194 | 195 | ; Remove registry keys 196 | DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\GW2RPC" 197 | DeleteRegKey HKLM SOFTWARE\GW2RPC 198 | 199 | ; Remove files and uninstaller 200 | Delete "$INSTDIR\gw2rpc.exe" 201 | Delete "$INSTDIR\config.ini" 202 | Delete "$INSTDIR\Launch_GW2_with_RPC.exe" 203 | Delete $INSTDIR\uninstall.exe 204 | 205 | ; Remove shortcuts, if any 206 | Delete "$desktop\gw2rpc.lnk" 207 | Delete "$desktop\Launch_GW2_with_RPC.lnk" 208 | Delete "$SMPROGRAMS\GW2RPC\*.lnk" 209 | Delete "$SMPROGRAMS\Startup\gw2rpc.lnk" 210 | 211 | ; Remove directories 212 | RMDir "$SMPROGRAMS\GW2RPC" 213 | RMDir "$INSTDIR" 214 | 215 | SectionEnd 216 | 217 | -------------------------------------------------------------------------------- /gw2rpc/api.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | import requests 4 | 5 | from .settings import config 6 | 7 | log = logging.getLogger() 8 | 9 | 10 | class APIError(Exception): 11 | def __init__(self, code): 12 | self.code = code 13 | 14 | 15 | class GW2Api: 16 | def __init__(self, key=None): 17 | def check_key(key): 18 | try: 19 | res = self._call_api("tokeninfo", key=key) 20 | required_permissions = ["characters", "builds", "account"] 21 | for p in required_permissions: 22 | if p not in res["permissions"]: 23 | log.warning( 24 | "API key missing required permission: {}".format( 25 | p)) 26 | return False 27 | log.info("API key verified") 28 | return True 29 | except APIError: 30 | return False 31 | 32 | def get_account_and_world(): 33 | try: 34 | res = self._call_api("account") 35 | ep = "worlds/{}".format(res["world"]) 36 | return res["name"], self._call_api(ep)["name"] 37 | except (APIError, KeyError): 38 | return None, None 39 | 40 | self.__session = requests.Session() 41 | self._base_url = "https://api.guildwars2.com/v2/" 42 | self.__headers = { 43 | 'User-Agent': "GW2RPC - Discord Rich Presence addon", 44 | 'Accept': 'application/json' 45 | } 46 | self._authenticated = False 47 | if key: 48 | if check_key(key): 49 | self.__headers.update(Authorization="Bearer " + key) 50 | self._authenticated = True 51 | self.__session.headers.update(self.__headers) 52 | if self._authenticated: 53 | self.account, self.world = get_account_and_world() 54 | else: 55 | self.account, self.world = None, None 56 | 57 | self.guild_cache = {} 58 | 59 | def get_map_info(self, map_id): 60 | return self._call_api("maps/" + str(map_id)) 61 | 62 | def get_continent_info(self, map_info): 63 | ep = ("continents/{continent_id}/floors/{default_floor}/regi" 64 | "ons/{region_id}/maps/{id}".format(**map_info)) 65 | return self._call_api(ep) 66 | 67 | def get_character(self, name): 68 | if not self._authenticated: 69 | return None 70 | return self._call_api("characters/" + name) 71 | 72 | def get_guild(self, gid): 73 | if gid in self.guild_cache.keys(): 74 | return {'tag': self.guild_cache[gid], 'id': gid} 75 | g = self._call_api("guild/" + gid) 76 | self.guild_cache[g["id"]] = g["tag"] 77 | return g 78 | 79 | def _call_api(self, endpoint, *, key=None): 80 | url = self._base_url + endpoint + "?lang=" + config.lang 81 | log.debug(f"Calling {endpoint}, {key}") 82 | try: 83 | if key: 84 | headers = {**self.__headers, **{"Authorization": "Bearer " + key}} 85 | r = requests.get(url, headers=headers) 86 | else: 87 | r = self.__session.get(url) 88 | except: 89 | log.error(f"Connection to {url} failed. Check connection.") 90 | raise APIError(1) 91 | 92 | if r.status_code != 200: 93 | raise APIError(r.status_code) 94 | return r.json() 95 | 96 | 97 | class MultiApi: 98 | def __init__(self, keys): 99 | self._unauthenticated_client = GW2Api() 100 | self._clients = [GW2Api(k) for k in keys] 101 | self._clients = [c for c in self._clients if c._authenticated] 102 | self._authenticated = len(self._clients) != 0 103 | self._last_used_client = None 104 | self.account, self.world = ( 105 | self._clients[0].account, 106 | self._clients[0].world) if self._authenticated else (None, None) 107 | 108 | def get_map_info(self, map_id): 109 | return self._unauthenticated_client.get_map_info(map_id) 110 | 111 | def get_continent_info(self, map_info): 112 | return self._unauthenticated_client.get_continent_info(map_info) 113 | 114 | def get_character(self, name): 115 | if self._last_used_client: 116 | try: 117 | return self._last_used_client.get_character(name) 118 | except APIError: 119 | pass 120 | for client in self._clients: 121 | try: 122 | c = client.get_character(name) 123 | self._last_used_client = client 124 | self.account, self.world = client.account, client.world 125 | return c 126 | except APIError: 127 | pass 128 | # intentionally cause an error as no API key matches 129 | return self._unauthenticated_client.get_character(name) 130 | 131 | def get_guild(self, gid): 132 | if self._last_used_client: 133 | return self._last_used_client.get_guild(gid) 134 | # intentionally cause an error as there should have been a character request first 135 | return self._unauthenticated_client.get_guild(gid) 136 | 137 | 138 | api = MultiApi(config.api_keys) 139 | -------------------------------------------------------------------------------- /gw2rpc/character.py: -------------------------------------------------------------------------------- 1 | from .api import api 2 | 3 | PROFESSIONS = { 4 | 1: "Guardian", 5 | 2: "Warrior", 6 | 3: "Engineer", 7 | 4: "Ranger", 8 | 5: "Thief", 9 | 6: "Elementalist", 10 | 7: "Mesmer", 11 | 8: "Necromancer", 12 | 9: "Revenant", 13 | 10: "Jade Bot" 14 | } 15 | 16 | RACES = {0: "Asura", 1: "Charr", 2: "Human", 3: "Norn", 4: "Sylvari", 5: "Jade Bot"} 17 | 18 | ELITESPECS = { 19 | 5: "Druid", 20 | 7: "Daredevil", 21 | 18: "Berserker", 22 | 27: "Dragonhunter", 23 | 34: "Reaper", 24 | 40: "Chronomancer", 25 | 43: "Scrapper", 26 | 48: "Tempest", 27 | 52: "Herald", 28 | 55: "Soulbeast", 29 | 56: "Weaver", 30 | 57: "Holosmith", 31 | 58: "Deadeye", 32 | 59: "Mirage", 33 | 60: "Scourge", 34 | 61: "Spellbreaker", 35 | 62: "Firebrand", 36 | 63: "Renegade", 37 | 64: "Harbinger", 38 | 65: "Willbender", 39 | 66: "Virtuoso", 40 | 67: "Catalyst", 41 | 68: "Bladesworn", 42 | 69: "Vindicator", 43 | 70: "Mechanist", 44 | 71: "Specter", 45 | 72: "Untamed" 46 | } 47 | 48 | 49 | class Character: 50 | def __init__(self, mumble_data, query_guild=True): 51 | self.__mumble_data = mumble_data 52 | self.name = mumble_data["name"] 53 | try: 54 | self.race = RACES[mumble_data["race"]] 55 | except KeyError: 56 | self.race = "" 57 | self.__api_info = None 58 | 59 | if query_guild and api._authenticated: 60 | self.__api_info = api.get_character(self.name) 61 | 62 | self.profession = self.get_elite_spec() 63 | if self.profession: 64 | self.profession_icon = "prof_{}".format( 65 | self.profession.lower().replace(" ", "")) 66 | else: 67 | self.profession = "" 68 | self.profession_icon = "gw2rpclogo" 69 | self.guild_tag = self._get_guild_tag() 70 | 71 | def get_elite_spec(self): 72 | if self.__mumble_data["spec"] not in ELITESPECS.keys(): 73 | # Meaning that its a core class, fall back 74 | try: 75 | return PROFESSIONS[self.__mumble_data["profession"]] 76 | except KeyError: 77 | return None 78 | else: 79 | return ELITESPECS[self.__mumble_data["spec"]] 80 | 81 | def _get_guild_tag(self): 82 | tag = "" 83 | if self.__api_info: 84 | gid = self.__api_info.get("guild") 85 | if gid: 86 | try: 87 | res = api.get_guild(gid) 88 | tag = " [{}]".format(res["tag"]) 89 | except: 90 | pass 91 | return tag 92 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/__init__.py: -------------------------------------------------------------------------------- 1 | from . import sdk 2 | from .achievement import AchievementManager 3 | from .activity import ActivityManager 4 | from .application import ApplicationManager 5 | from .discord import Discord 6 | from .enum import ( 7 | ActivityActionType, ActivityJoinRequestReply, ActivityType, CreateFlags, 8 | EntitlementType, ImageType, InputModeType, LobbySearchCast, 9 | LobbySearchComparison, LobbySearchDistance, LobbyType, LogLevel, 10 | PremiumType, RelationshipType, Result, SkuType, Status, UserFlag) 11 | from .event import bind_events 12 | from .exception import DiscordException, exceptions, get_exception 13 | from .image import ImageManager 14 | from .lobby import (LobbyManager, LobbyMemberTransaction, LobbySearchQuery, 15 | LobbyTransaction) 16 | from .model import ( 17 | Activity, ActivityAssets, ActivityParty, ActivitySecrets, 18 | ActivityTimestamps, Entitlement, FileStat, ImageDimensions, ImageHandle, 19 | InputMode, Lobby, Model, OAuth2Token, PartySize, Presence, Relationship, 20 | Sku, SkuPrice, User, UserAchievement) 21 | from .network import NetworkManager 22 | from .overlay import OverlayManager 23 | from .relationship import RelationshipManager 24 | from .storage import StorageManager 25 | from .store import StoreManager 26 | from .user import UserManager 27 | from .voice import VoiceManager 28 | 29 | __all__ = [ 30 | "sdk", 31 | "AchievementManager", 32 | "ActivityManager", 33 | "ApplicationManager", 34 | "Discord", 35 | "ActivityActionType", "ActivityJoinRequestReply", "ActivityType", "CreateFlags", 36 | "EntitlementType", "ImageType", "InputModeType", "LobbySearchCast", 37 | "LobbySearchComparison", "LobbySearchDistance", "LobbyType", "LogLevel", 38 | "PremiumType", "RelationshipType", "Result", "SkuType", "Status", "UserFlag", 39 | "bind_events", 40 | "DiscordException", "exceptions", "get_exception", 41 | "ImageManager", 42 | "LobbyManager", "LobbyMemberTransaction", "LobbySearchQuery", 43 | "LobbyTransaction", 44 | "Activity", "ActivityAssets", "ActivityParty", "ActivitySecrets", 45 | "ActivityTimestamps", "Entitlement", "FileStat", "ImageDimensions", "ImageHandle", 46 | "InputMode", "Lobby", "Model", "OAuth2Token", "PartySize", "Presence", "Relationship", 47 | "Sku", "SkuPrice", "User", "UserAchievement", 48 | "NetworkManager", 49 | "OverlayManager", 50 | "RelationshipManager", 51 | "StorageManager", 52 | "StoreManager", 53 | "UserManager", 54 | "VoiceManager", 55 | ] 56 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/achievement.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import typing as t 3 | 4 | from . import sdk 5 | from .enum import Result 6 | from .event import bind_events 7 | from .exception import get_exception 8 | from .model import UserAchievement 9 | 10 | 11 | class AchievementManager: 12 | _internal: sdk.IDiscordAchievementManager = None 13 | _garbage: t.List[t.Any] 14 | _events: sdk.IDiscordAchievementEvents 15 | 16 | def __init__(self): 17 | self._garbage = [] 18 | self._events = bind_events( 19 | sdk.IDiscordAchievementEvents, 20 | self._on_user_achievement_update 21 | ) 22 | 23 | def _on_user_achievement_update(self, event_data, user_achievement): 24 | self.on_user_achievement_update(UserAchievement(copy=user_achievement.contents)) 25 | 26 | def set_user_achievement( 27 | self, 28 | achievement_id: int, 29 | percent_complete: int, 30 | callback: t.Callable[[Result], None] 31 | ) -> None: 32 | """ 33 | Updates the current user's status for a given achievement. 34 | 35 | Returns discordsdk.enum.Result via callback. 36 | """ 37 | def c_callback(callback_data, result): 38 | self._garbage.remove(c_callback) 39 | result = Result(result) 40 | callback(result) 41 | 42 | c_callback = self._internal.set_user_achievement.argtypes[-1](c_callback) 43 | self._garbage.append(c_callback) # prevent it from being garbage collected 44 | 45 | self._internal.set_user_achievement( 46 | self._internal, 47 | achievement_id, 48 | percent_complete, 49 | ctypes.c_void_p(), 50 | c_callback 51 | ) 52 | 53 | def fetch_user_achievements(self, callback: t.Callable[[Result], None]) -> None: 54 | """ 55 | Loads a stable list of the current user's achievements to iterate over. 56 | 57 | Returns discordsdk.enum.Result via callback. 58 | """ 59 | def c_callback(callback_data, result): 60 | self._garbage.remove(c_callback) 61 | result = Result(result) 62 | callback(result) 63 | 64 | c_callback = self._internal.fetch_user_achievements.argtypes[-1](c_callback) 65 | self._garbage.append(c_callback) # prevent it from being garbage collected 66 | 67 | self._internal.fetch_user_achievements(self._internal, ctypes.c_void_p(), c_callback) 68 | 69 | def count_user_achievements(self) -> int: 70 | """ 71 | Counts the list of a user's achievements for iteration. 72 | """ 73 | count = ctypes.c_int32() 74 | self._internal.count_user_achievements(self._internal, count) 75 | return count.value 76 | 77 | def get_user_achievement_at(self, index: int) -> UserAchievement: 78 | """ 79 | Gets the user's achievement at a given index of their list of achievements. 80 | """ 81 | achievement = sdk.DiscordUserAchievement() 82 | result = Result(self._internal.get_user_achievement_at( 83 | self._internal, 84 | index, 85 | achievement 86 | )) 87 | if result != Result.ok: 88 | raise get_exception(result) 89 | 90 | return UserAchievement(internal=achievement) 91 | 92 | def get_user_achievement(self, achievement_id: int) -> None: 93 | """ 94 | Gets the user achievement for the given achievement id. 95 | """ 96 | achievement = sdk.DiscordUserAchievement() 97 | result = Result(self._internal.get_user_achievement( 98 | self._internal, 99 | achievement_id, 100 | achievement 101 | )) 102 | if result != Result.ok: 103 | raise get_exception(result) 104 | 105 | return UserAchievement(internal=achievement) 106 | 107 | def on_user_achievement_update(self, achievement: UserAchievement) -> None: 108 | """ 109 | Fires when an achievement is updated for the currently connected user 110 | """ 111 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/activity.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import typing as t 3 | 4 | from . import sdk 5 | from .enum import ActivityActionType, ActivityJoinRequestReply, Result 6 | from .event import bind_events 7 | from .model import Activity, User 8 | 9 | 10 | class ActivityManager: 11 | _internal: sdk.IDiscordActivityManager = None 12 | _garbage: t.List[t.Any] 13 | _events: sdk.IDiscordActivityEvents 14 | 15 | def __init__(self): 16 | self._garbage = [] 17 | self._events = bind_events( 18 | sdk.IDiscordActivityEvents, 19 | self._on_activity_join, 20 | self._on_activity_spectate, 21 | self._on_activity_join_request, 22 | self._on_activity_invite 23 | ) 24 | 25 | def _on_activity_join(self, event_data, secret): 26 | self.on_activity_join(secret.decode("utf8")) 27 | 28 | def _on_activity_spectate(self, event_data, secret): 29 | self.on_activity_spectate(secret.decode("utf8")) 30 | 31 | def _on_activity_join_request(self, event_data, user): 32 | self.on_activity_join_request(User(copy=user.contents)) 33 | 34 | def _on_activity_invite(self, event_data, type, user, activity): 35 | self.on_activity_invite(type, User(copy=user.contents), Activity(copy=activity.contents)) 36 | 37 | def register_command(self, command: str) -> Result: 38 | """ 39 | Registers a command by which Discord can launch your game. 40 | """ 41 | result = Result(self._internal.register_command(self._internal, command.encode("utf8"))) 42 | return result 43 | 44 | def register_steam(self, steam_id: int) -> Result: 45 | """ 46 | Registers your game's Steam app id for the protocol `steam://run-game-id/`. 47 | """ 48 | result = Result(self._internal.register_steam(self._internal, steam_id)) 49 | return result 50 | 51 | def update_activity(self, activity: Activity, callback: t.Callable[[Result], None]) -> None: 52 | """ 53 | Set a user's presence in Discord to a new activity. 54 | 55 | Returns discordsdk.enum.Result (int) via callback. 56 | """ 57 | def c_callback(callback_data, result): 58 | self._garbage.remove(c_callback) 59 | result = Result(result) 60 | callback(result) 61 | 62 | c_callback = self._internal.update_activity.argtypes[-1](c_callback) 63 | self._garbage.append(c_callback) # prevent it from being garbage collected 64 | 65 | self._internal.update_activity( 66 | self._internal, 67 | activity._internal, 68 | ctypes.c_void_p(), 69 | c_callback 70 | ) 71 | 72 | def clear_activity(self, callback: t.Callable[[Result], None]) -> None: 73 | """ 74 | Clears a user's presence in Discord to make it show nothing. 75 | 76 | Returns discordsdk.enum.Result (int) via callback. 77 | """ 78 | def c_callback(callback_data, result): 79 | self._garbage.remove(c_callback) 80 | result = Result(result) 81 | callback(result) 82 | 83 | c_callback = self._internal.clear_activity.argtypes[-1](c_callback) 84 | self._garbage.append(c_callback) # prevent it from being garbage collected 85 | 86 | self._internal.clear_activity(self._internal, ctypes.c_void_p(), c_callback) 87 | 88 | 89 | def send_request_reply( 90 | self, 91 | user_id: int, 92 | reply: ActivityJoinRequestReply, 93 | callback: t.Callable[[Result], None] 94 | ) -> None: 95 | """ 96 | Sends a reply to an Ask to Join request. 97 | 98 | Returns discordsdk.enum.Result (int) via callback. 99 | """ 100 | def c_callback(callback_data, result): 101 | self._garbage.remove(c_callback) 102 | result = Result(result) 103 | callback(result) 104 | 105 | c_callback = self._internal.send_request_reply.argtypes[-1](c_callback) 106 | self._garbage.append(c_callback) # prevent it from being garbage collected 107 | 108 | self._internal.send_request_reply( 109 | self._internal, 110 | user_id, 111 | reply, 112 | ctypes.c_void_p(), 113 | c_callback 114 | ) 115 | 116 | def send_invite( 117 | self, 118 | user_id: int, 119 | type: ActivityActionType, 120 | content: str, 121 | callback: t.Callable[[Result], None] 122 | ) -> None: 123 | """ 124 | Sends a game invite to a given user. 125 | 126 | Returns discordsdk.enum.Result (int) via callback. 127 | """ 128 | def c_callback(callback_data, result): 129 | self._garbage.remove(c_callback) 130 | result = Result(result) 131 | callback(result) 132 | 133 | c_callback = self._internal.send_invite.argtypes[-1](c_callback) 134 | self._garbage.append(c_callback) # prevent it from being garbage collected 135 | 136 | self._internal.send_invite( 137 | self._internal, 138 | user_id, 139 | type, 140 | content.encode("utf8"), 141 | ctypes.c_void_p(), 142 | c_callback 143 | ) 144 | 145 | def accept_invite(self, user_id: int, callback: t.Callable[[Result], None]) -> None: 146 | """ 147 | Accepts a game invitation from a given userId. 148 | 149 | Returns discordsdk.enum.Result (int) via callback. 150 | """ 151 | def c_callback(callback_data, result): 152 | self._garbage.remove(c_callback) 153 | result = Result(result) 154 | callback(result) 155 | 156 | c_callback = self._internal.accept_invite.argtypes[-1](c_callback) 157 | self._garbage.append(c_callback) # prevent it from being garbage collected 158 | 159 | self._internal.accept_invite(self._internal, user_id, ctypes.c_void_p(), c_callback) 160 | 161 | def on_activity_join(self, join_secret: str) -> None: 162 | """ 163 | Fires when a user accepts a game chat invite or receives confirmation from Asking to Join. 164 | """ 165 | 166 | def on_activity_spectate(self, spectate_secret: str) -> None: 167 | """ 168 | Fires when a user accepts a spectate chat invite or clicks the Spectate button on a user's 169 | profile. 170 | """ 171 | 172 | def on_activity_join_request(self, user: User) -> None: 173 | """ 174 | Fires when a user asks to join the current user's game. 175 | """ 176 | 177 | def on_activity_invite(self, type: ActivityActionType, user: User, activity: Activity) -> None: 178 | """ 179 | Fires when the user receives a join or spectate invite. 180 | """ 181 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/application.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import typing as t 3 | 4 | from . import sdk 5 | from .enum import Result 6 | from .model import OAuth2Token 7 | 8 | 9 | class ApplicationManager: 10 | _internal: sdk.IDiscordApplicationManager = None 11 | _garbage: t.List[t.Any] 12 | _events: sdk.IDiscordApplicationEvents = None 13 | 14 | def __init__(self): 15 | self._garbage = [] 16 | 17 | def get_current_locale(self) -> str: 18 | """ 19 | Get the locale the current user has Discord set to. 20 | """ 21 | locale = sdk.DiscordLocale() 22 | self._internal.get_current_locale(self._internal, locale) 23 | return locale.value.decode("utf8") 24 | 25 | def get_current_branch(self) -> str: 26 | """ 27 | Get the name of pushed branch on which the game is running. 28 | """ 29 | branch = sdk.DiscordBranch() 30 | self._internal.get_current_branch(self._internal, branch) 31 | return branch.value.decode("utf8") 32 | 33 | def get_oauth2_token( 34 | self, 35 | callback: t.Callable[[Result, t.Optional[OAuth2Token]], None] 36 | ) -> None: 37 | """ 38 | Retrieve an oauth2 bearer token for the current user. 39 | 40 | Returns discordsdk.enum.Result (int) and OAuth2Token (str) via callback. 41 | """ 42 | def c_callback(callback_data, result, oauth2_token): 43 | self._garbage.remove(c_callback) 44 | if result == Result.ok: 45 | callback(result, OAuth2Token(copy=oauth2_token.contents)) 46 | else: 47 | callback(result, None) 48 | 49 | c_callback = self._internal.get_oauth2_token.argtypes[-1](c_callback) 50 | self._garbage.append(c_callback) # prevent it from being garbage collected 51 | 52 | self._internal.get_oauth2_token(self._internal, ctypes.c_void_p(), c_callback) 53 | 54 | def validate_or_exit(self, callback: t.Callable[[Result], None]) -> None: 55 | """ 56 | Checks if the current user has the entitlement to run this game. 57 | 58 | Returns discordsdk.enum.Result (int) via callback. 59 | """ 60 | def c_callback(callback_data, result): 61 | self._garbage.remove(c_callback) 62 | callback(result) 63 | 64 | c_callback = self._internal.validate_or_exit.argtypes[-1](c_callback) 65 | self._garbage.append(c_callback) # prevent it from being garbage collected 66 | 67 | self._internal.validate_or_exit(self._internal, ctypes.c_void_p(), c_callback) 68 | 69 | def get_ticket(self, callback: t.Callable[[Result, t.Optional[str]], None]) -> None: 70 | """ 71 | Get the signed app ticket for the current user. 72 | 73 | Returns discordsdk.Enum.Result (int) and str via callback. 74 | """ 75 | def c_callback(callback_data, result, data): 76 | self._garbage.remove(c_callback) 77 | if result == Result.ok: 78 | callback(result, data.contents.value.decode("utf8")) 79 | else: 80 | callback(result, None) 81 | 82 | c_callback = self._internal.get_ticket.argtypes[-1](c_callback) 83 | self._garbage.append(c_callback) # prevent it from being garbage collected 84 | 85 | self._internal.get_ticket(self._internal, ctypes.c_void_p(), c_callback) 86 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/discord.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import typing as t 3 | 4 | from . import sdk 5 | from .achievement import AchievementManager 6 | from .activity import ActivityManager 7 | from .application import ApplicationManager 8 | from .enum import CreateFlags, LogLevel, Result 9 | from .exception import get_exception 10 | from .image import ImageManager 11 | from .lobby import LobbyManager 12 | from .network import NetworkManager 13 | from .overlay import OverlayManager 14 | from .relationship import RelationshipManager 15 | from .storage import StorageManager 16 | from .store import StoreManager 17 | from .user import UserManager 18 | from .voice import VoiceManager 19 | 20 | 21 | class Discord: 22 | _garbage: t.List[t.Any] 23 | 24 | core: sdk.IDiscordCore = None 25 | 26 | def __init__(self, client_id: int, flags: CreateFlags): 27 | self._garbage = [] 28 | 29 | self._activity_manager = ActivityManager() 30 | self._relationship_manager = RelationshipManager() 31 | self._image_manager = ImageManager() 32 | self._user_manager = UserManager() 33 | self._lobby_manager = LobbyManager() 34 | self._network_manager = NetworkManager() 35 | self._overlay_manager = OverlayManager() 36 | self._application_manager = ApplicationManager() 37 | self._storage_manager = StorageManager() 38 | self._store_manager = StoreManager() 39 | self._voice_manager = VoiceManager() 40 | self._achievement_manager = AchievementManager() 41 | 42 | version = sdk.DiscordVersion(2) 43 | 44 | params = sdk.DiscordCreateParams() 45 | params.client_id = client_id 46 | params.flags = flags 47 | 48 | sdk.DiscordCreateParamsSetDefault(params) 49 | params.activity_events = self._activity_manager._events 50 | params.relationship_events = self._relationship_manager._events 51 | params.image_events = self._image_manager._events 52 | params.user_events = self._user_manager._events 53 | params.lobby_events = self._lobby_manager._events 54 | params.network_events = self._network_manager._events 55 | params.overlay_events = self._overlay_manager._events 56 | params.application_events = self._application_manager._events 57 | params.storage_events = self._storage_manager._events 58 | params.store_events = self._store_manager._events 59 | params.voice_events = self._voice_manager._events 60 | params.achievement_events = self._achievement_manager._events 61 | 62 | pointer = ctypes.POINTER(sdk.IDiscordCore)() 63 | 64 | result = Result(sdk.DiscordCreate(version, ctypes.pointer(params), ctypes.pointer(pointer))) 65 | if result != Result.ok: 66 | raise get_exception(result) 67 | 68 | self.core = pointer.contents 69 | 70 | 71 | def __del__(self): 72 | if self.core: 73 | self.core.destroy(self.core) 74 | self.core = None 75 | 76 | def dispose(self): 77 | self.core.dispose(self.core) 78 | 79 | 80 | def set_log_hook(self, min_level: LogLevel, hook: t.Callable[[LogLevel, str], None]) -> None: 81 | """ 82 | Registers a logging callback function with the minimum level of message to receive. 83 | """ 84 | def c_hook(hook_data, level, message): 85 | level = LogLevel(level) 86 | hook(level, message.decode("utf8")) 87 | 88 | c_hook = self.core.set_log_hook.argtypes[-1](c_hook) 89 | self._garbage.append(c_hook) 90 | 91 | self.core.set_log_hook(self.core, min_level.value, ctypes.c_void_p(), c_hook) 92 | 93 | def run_callbacks(self) -> None: 94 | """ 95 | Runs all pending SDK callbacks. 96 | """ 97 | result = Result(self.core.run_callbacks(self.core)) 98 | if result != Result.ok: 99 | raise get_exception(result) 100 | 101 | def get_activity_manager(self) -> ActivityManager: 102 | """ 103 | Fetches an instance of the manager for interfacing with activies in the SDK. 104 | """ 105 | if not self._activity_manager._internal: 106 | self._activity_manager._internal = self.core.get_activity_manager(self.core).contents 107 | 108 | return self._activity_manager 109 | 110 | def get_relationship_manager(self) -> RelationshipManager: 111 | """ 112 | Fetches an instance of the manager for interfacing with relationships in the SDK. 113 | """ 114 | if not self._relationship_manager._internal: 115 | self._relationship_manager._internal = self.core.get_relationship_manager(self.core).contents # noqa: E501 116 | 117 | return self._relationship_manager 118 | 119 | def get_image_manager(self) -> ImageManager: 120 | """ 121 | Fetches an instance of the manager for interfacing with images in the SDK. 122 | """ 123 | if not self._image_manager._internal: 124 | self._image_manager._internal = self.core.get_image_manager(self.core).contents 125 | 126 | return self._image_manager 127 | 128 | def get_user_manager(self) -> UserManager: 129 | """ 130 | Fetches an instance of the manager for interfacing with users in the SDK. 131 | """ 132 | if not self._user_manager._internal: 133 | self._user_manager._internal = self.core.get_user_manager(self.core).contents 134 | 135 | return self._user_manager 136 | 137 | def get_lobby_manager(self) -> LobbyManager: 138 | """ 139 | Fetches an instance of the manager for interfacing with lobbies in the SDK. 140 | """ 141 | if not self._lobby_manager._internal: 142 | self._lobby_manager._internal = self.core.get_lobby_manager(self.core).contents 143 | 144 | return self._lobby_manager 145 | 146 | def get_network_manager(self) -> NetworkManager: 147 | """ 148 | Fetches an instance of the manager for interfacing with networking in the SDK. 149 | """ 150 | if not self._network_manager._internal: 151 | self._network_manager._internal = self.core.get_network_manager(self.core).contents 152 | 153 | return self._network_manager 154 | 155 | def get_overlay_manager(self) -> OverlayManager: 156 | """ 157 | Fetches an instance of the manager for interfacing with the overlay in the SDK. 158 | """ 159 | if not self._overlay_manager._internal: 160 | self._overlay_manager._internal = self.core.get_overlay_manager(self.core).contents 161 | 162 | return self._overlay_manager 163 | 164 | def get_application_manager(self) -> ApplicationManager: 165 | """ 166 | Fetches an instance of the manager for interfacing with applications in the SDK. 167 | """ 168 | if not self._application_manager._internal: 169 | self._application_manager._internal = self.core.get_application_manager(self.core).contents # noqa: E501 170 | 171 | return self._application_manager 172 | 173 | def get_storage_manager(self) -> StorageManager: 174 | """ 175 | Fetches an instance of the manager for interfacing with storage in the SDK. 176 | """ 177 | if not self._storage_manager._internal: 178 | self._storage_manager._internal = self.core.get_storage_manager(self.core).contents 179 | 180 | return self._storage_manager 181 | 182 | def get_store_manager(self) -> StoreManager: 183 | """ 184 | Fetches an instance of the manager for interfacing with SKUs and Entitlements in the SDK. 185 | """ 186 | if not self._store_manager._internal: 187 | self._store_manager._internal = self.core.get_store_manager(self.core).contents 188 | 189 | return self._store_manager 190 | 191 | def get_voice_manager(self) -> VoiceManager: 192 | """ 193 | Fetches an instance of the manager for interfacing with voice chat in the SDK. 194 | """ 195 | if not self._voice_manager._internal: 196 | self._voice_manager._internal = self.core.get_voice_manager(self.core).contents 197 | 198 | return self._voice_manager 199 | 200 | def get_achievement_manager(self) -> AchievementManager: 201 | """ 202 | Fetches an instance of the manager for interfacing with achievements in the SDK. 203 | """ 204 | if not self._achievement_manager._internal: 205 | self._achievement_manager._internal = self.core.get_achievement_manager(self.core).contents # noqa: E501 206 | 207 | return self._achievement_manager 208 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/enum.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | if sys.version_info >= (3, 6): 4 | from enum import IntEnum, IntFlag 5 | else: 6 | from enum import IntEnum, IntEnum as IntFlag 7 | 8 | 9 | class Result(IntEnum): 10 | ok = 0 11 | service_unavailable = 1 12 | invalid_version = 2 13 | lock_failed = 3 14 | internal_error = 4 15 | invalid_payload = 5 16 | invalid_command = 6 17 | invalid_permissions = 7 18 | not_fetched = 8 19 | not_found = 9 20 | conflict = 10 21 | invalid_secret = 11 22 | invalid_join_secret = 12 23 | no_eligible_activity = 13 24 | invalid_invite = 14 25 | not_authenticated = 15 26 | invalid_access_token = 16 27 | application_mismatch = 17 28 | invalid_data_url = 18 29 | invalid_base_64 = 19 30 | not_filtered = 20 31 | lobby_full = 21 32 | invalid_lobby_secret = 22 33 | invalid_filename = 23 34 | invalid_file_size = 24 35 | invalid_entitlement = 25 36 | not_installed = 26 37 | not_running = 27 38 | insufficient_buffer = 28 39 | purchase_canceled = 29 40 | invalid_guild = 30 41 | invalid_event = 31 42 | invalid_channel = 32 43 | invalid_origin = 33 44 | rate_limited = 34 45 | oauth2_error = 35 46 | select_channel_timeout = 36 47 | get_guild_timeout = 37 48 | select_voice_force_required = 38 49 | capture_shortcut_already_listening = 39 50 | unauthorized_for_achievement = 40 51 | invalid_gift_code = 41 52 | purchase_error = 42 53 | transaction_aborted = 43 54 | drawing_init_failed = 44 55 | 56 | 57 | class LogLevel(IntEnum): 58 | error = 0 59 | warning = 1 60 | info = 2 61 | debug = 3 62 | 63 | 64 | class CreateFlags(IntFlag): 65 | default = 0 66 | no_require_discord = 1 67 | 68 | 69 | class UserFlag(IntFlag): 70 | partner = 2 71 | hype_squad_events = 4 72 | hype_squad_house_1 = 64 73 | hype_squad_house_2 = 128 74 | hype_squad_house_3 = 256 75 | 76 | 77 | class PremiumType(IntEnum): 78 | none_ = 0 79 | tier_1 = 1 80 | tier_2 = 2 81 | 82 | 83 | class ActivityType(IntEnum): 84 | playing = 0 85 | streaming = 1 86 | listening = 2 87 | custom = 4 88 | 89 | 90 | class ActivityJoinRequestReply(IntEnum): 91 | no = 0 92 | yes = 1 93 | ignore = 2 94 | 95 | 96 | class ActivityActionType(IntEnum): 97 | join = 1 98 | spectate = 2 99 | 100 | 101 | class RelationshipType(IntEnum): 102 | none_ = 0 103 | friend = 1 104 | blocked = 2 105 | pending_incoming = 3 106 | pending_outgoing = 4 107 | implicit = 5 108 | 109 | 110 | class Status(IntEnum): 111 | offline = 0 112 | online = 1 113 | idle = 2 114 | do_not_disturb = 3 115 | 116 | 117 | class ImageType(IntEnum): 118 | user = 0 119 | 120 | 121 | class LobbyType(IntEnum): 122 | private = 1 123 | public = 2 124 | 125 | 126 | class LobbySearchComparison(IntEnum): 127 | LessThanOrEqual = -2 128 | LessThan = -1 129 | Equal = 0 130 | GreaterThan = 1 131 | GreaterThanOrEqual = 2 132 | NotEqual = 3 133 | 134 | 135 | class LobbySearchCast(IntEnum): 136 | String = 1 137 | Number = 2 138 | 139 | 140 | class LobbySearchDistance(IntEnum): 141 | Local = 0 142 | Default = 1 143 | Extended = 2 144 | Global = 3 145 | 146 | 147 | class InputModeType(IntEnum): 148 | VoiceActivity = 0 149 | PushToTalk = 1 150 | 151 | 152 | class SkuType(IntEnum): 153 | Application = 1 154 | DLC = 2 155 | Consumable = 3 156 | Bundle = 4 157 | 158 | 159 | class EntitlementType(IntEnum): 160 | Purchase = 1 161 | PremiumSubscription = 2 162 | DeveloperGift = 3 163 | TestModePurchase = 4 164 | FreePurchase = 5 165 | UserGift = 6 166 | PremiumPurchase = 7 167 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/event.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import typing as t 3 | 4 | 5 | def bind_events(structure: ctypes.Structure, *methods: t.List[t.Callable[..., None]]): 6 | contents = structure() 7 | for index, (name, func) in enumerate(structure._fields_): 8 | setattr(contents, name, func(methods[index])) 9 | 10 | pointer = ctypes.pointer(contents) 11 | return pointer 12 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/exception.py: -------------------------------------------------------------------------------- 1 | from .enum import Result 2 | 3 | 4 | class DiscordException(Exception): 5 | pass 6 | 7 | 8 | exceptions = {} 9 | 10 | # we dynamically create the exceptions 11 | for res in Result: 12 | exception = type(res.name, (DiscordException,), {}) 13 | 14 | globals()[res.name] = exception 15 | exceptions[res] = exception 16 | 17 | 18 | def get_exception(result): 19 | return exceptions.get(result, DiscordException)("result " + str(result.value)) 20 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/image.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import typing as t 3 | 4 | from . import sdk 5 | from .enum import Result 6 | from .exception import get_exception 7 | from .model import ImageDimensions, ImageHandle 8 | 9 | 10 | class ImageManager: 11 | _internal: sdk.IDiscordImageManager = None 12 | _garbage: t.List[t.Any] 13 | _events: sdk.IDiscordImageEvents = None 14 | 15 | def __init__(self): 16 | self._garbage = [] 17 | 18 | def fetch( 19 | self, 20 | handle: ImageHandle, 21 | refresh: bool, 22 | callback: t.Callable[[Result, t.Optional[ImageHandle]], None] 23 | ) -> None: 24 | """ 25 | Prepares an image to later retrieve data about it. 26 | 27 | Returns discordsdk.enum.Result (int) and ImageHandle via callback. 28 | """ 29 | def c_callback(callback_data, result, handle): 30 | self._garbage.remove(c_callback) 31 | result = Result(result) 32 | if result == Result.ok: 33 | callback(result, ImageHandle(internal=handle)) 34 | else: 35 | callback(result, None) 36 | 37 | c_callback = self._internal.fetch.argtypes[-1](c_callback) 38 | self._garbage.append(c_callback) # prevent it from being garbage collected 39 | 40 | self._internal.fetch( 41 | self._internal, 42 | handle._internal, 43 | refresh, 44 | ctypes.c_void_p(), 45 | c_callback 46 | ) 47 | 48 | def get_dimensions(self, handle: ImageHandle) -> ImageDimensions: 49 | """ 50 | Gets the dimension for the given user's avatar's source image 51 | """ 52 | dimensions = sdk.DiscordImageDimensions() 53 | result = Result(self._internal.get_dimensions(self._internal, handle._internal, dimensions)) 54 | if result != Result.ok: 55 | raise get_exception(result) 56 | 57 | return ImageDimensions(internal=dimensions) 58 | 59 | def get_data(self, handle: ImageHandle) -> bytes: 60 | """ 61 | Gets the image data for a given user's avatar. 62 | """ 63 | dimensions = self.get_dimensions(handle) 64 | buffer = (ctypes.c_uint8 * (dimensions.width * dimensions.height * 4))() 65 | 66 | result = Result(self._internal.get_data( 67 | self._internal, handle._internal, buffer, len(buffer))) 68 | if result != Result.ok: 69 | raise get_exception(result) 70 | 71 | return bytes(buffer) 72 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/model.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | from enum import Enum 3 | 4 | from . import sdk 5 | from .enum import (EntitlementType, ImageType, InputModeType, LobbyType, 6 | RelationshipType, SkuType, Status) 7 | 8 | 9 | class Model: 10 | def __init__(self, **kwargs): 11 | self._internal = kwargs.get("internal", self._struct_()) 12 | if "copy" in kwargs: 13 | ctypes.memmove( 14 | ctypes.byref(self._internal), 15 | ctypes.byref(kwargs["copy"]), 16 | ctypes.sizeof(self._struct_) 17 | ) 18 | 19 | self._fields = {} 20 | 21 | for field, ftype in self._fields_: 22 | self._fields[field] = ftype 23 | if issubclass(ftype, Model): 24 | setattr(self, "_" + field, ftype(internal=getattr(self._internal, field))) 25 | 26 | def __getattribute__(self, key): 27 | if key.startswith("_"): 28 | return super().__getattribute__(key) 29 | else: 30 | ftype = self._fields[key] 31 | value = getattr(self._internal, key) 32 | if ftype == int: 33 | return int(value) 34 | elif ftype == str: 35 | return value.decode("utf8") 36 | elif ftype == bool: 37 | return bool(value) 38 | elif issubclass(ftype, Model): 39 | return getattr(self, "_" + key) 40 | elif issubclass(ftype, Enum): 41 | return ftype(int(value)) 42 | else: 43 | raise TypeError(ftype) 44 | 45 | def __setattr__(self, key, value): 46 | if key.startswith("_"): 47 | super().__setattr__(key, value) 48 | else: 49 | ftype = self._fields[key] 50 | if ftype == int: 51 | value = int(value) 52 | setattr(self._internal, key, value) 53 | elif ftype == str: 54 | value = value.encode("utf8") 55 | setattr(self._internal, key, value) 56 | elif ftype == bool: 57 | value = bool(value) 58 | setattr(self._internal, key, value) 59 | elif issubclass(ftype, Model): 60 | setattr(self, "_" + key, value) 61 | setattr(self._internal, key, value._internal) 62 | elif issubclass(ftype, Enum): 63 | setattr(self._internal, key, value.value) 64 | else: 65 | raise TypeError(ftype) 66 | 67 | def __dir__(self): 68 | return super().__dir__() + list(self._fields.keys()) 69 | 70 | 71 | class User(Model): 72 | _struct_ = sdk.DiscordUser 73 | _fields_ = [ 74 | ("id", int), 75 | ("username", str), 76 | ("discriminator", str), 77 | ("avatar", str), 78 | ("bot", bool), 79 | ] 80 | 81 | id: int 82 | username: str 83 | discriminator: str 84 | avatar: str 85 | bot: str 86 | 87 | 88 | class ActivityTimestamps(Model): 89 | _struct_ = sdk.DiscordActivityTimestamps 90 | _fields_ = [ 91 | ("start", int), 92 | ("end", int), 93 | ] 94 | 95 | start: int 96 | end: int 97 | 98 | 99 | class ActivityAssets(Model): 100 | _struct_ = sdk.DiscordActivityAssets 101 | _fields_ = [ 102 | ("large_image", str), 103 | ("large_text", str), 104 | ("small_image", str), 105 | ("small_text", str), 106 | ] 107 | 108 | large_image: str 109 | large_text: str 110 | small_image: str 111 | small_text: str 112 | 113 | 114 | class PartySize(Model): 115 | _struct_ = sdk.DiscordPartySize 116 | _fields_ = [ 117 | ("current_size", int), 118 | ("max_size", int), 119 | ] 120 | 121 | current_size: int 122 | max_size: int 123 | 124 | 125 | class ActivityParty(Model): 126 | _struct_ = sdk.DiscordActivityParty 127 | _fields_ = [ 128 | ("id", str), 129 | ("size", PartySize), 130 | ] 131 | 132 | id: str 133 | size: PartySize 134 | 135 | 136 | class ActivitySecrets(Model): 137 | _struct_ = sdk.DiscordActivitySecrets 138 | _fields_ = [ 139 | ("match", str), 140 | ("join", str), 141 | ("spectate", str), 142 | ] 143 | 144 | match: str 145 | join: str 146 | spectate: str 147 | 148 | 149 | class Activity(Model): 150 | _struct_ = sdk.DiscordActivity 151 | _fields_ = [ 152 | ("application_id", int), 153 | ("name", str), 154 | ("state", str), 155 | ("details", str), 156 | ("timestamps", ActivityTimestamps), 157 | ("assets", ActivityAssets), 158 | ("party", ActivityParty), 159 | ("secrets", ActivitySecrets), 160 | ("instance", bool), 161 | ] 162 | 163 | application_id: int 164 | name: str 165 | state: str 166 | details: str 167 | timestamps: ActivityTimestamps 168 | assets: ActivityAssets 169 | party: ActivityParty 170 | secrets: ActivitySecrets 171 | instance: bool 172 | 173 | 174 | class Presence(Model): 175 | _struct_ = sdk.DiscordPresence 176 | _fields_ = [ 177 | ("status", Status), 178 | ("activity", Activity), 179 | ] 180 | 181 | status: Status 182 | activity: Activity 183 | 184 | 185 | class Relationship(Model): 186 | _struct_ = sdk.DiscordRelationship 187 | _fields_ = [ 188 | ("type", RelationshipType), 189 | ("user", User), 190 | ("presence", Presence), 191 | ] 192 | 193 | type: RelationshipType 194 | user: User 195 | presence: Presence 196 | 197 | 198 | class ImageDimensions(Model): 199 | _struct_ = sdk.DiscordImageDimensions 200 | _fields_ = [ 201 | ("width", int), 202 | ("height", int), 203 | ] 204 | 205 | width: int 206 | height: int 207 | 208 | 209 | class ImageHandle(Model): 210 | _struct_ = sdk.DiscordImageHandle 211 | _fields_ = [ 212 | ("type", ImageType), 213 | ("id", int), 214 | ("size", int), 215 | ] 216 | 217 | type: ImageType 218 | id: int 219 | size: int 220 | 221 | 222 | class OAuth2Token(Model): 223 | _struct_ = sdk.DiscordOAuth2Token 224 | _fields_ = [ 225 | ("access_token", str), 226 | ("scopes", str), 227 | ("expires", int), 228 | ] 229 | 230 | access_token: str 231 | scopes: str 232 | expires: str 233 | 234 | 235 | class Lobby(Model): 236 | _struct_ = sdk.DiscordLobby 237 | _fields_ = [ 238 | ("id", int), 239 | ("type", LobbyType), 240 | ("owner_id", int), 241 | ("secret", str), 242 | ("capacity", int), 243 | ("locked", bool), 244 | ] 245 | 246 | id: int 247 | type: LobbyType 248 | owner_id: int 249 | secret: str 250 | capacity: int 251 | locked: bool 252 | 253 | 254 | class InputMode(Model): 255 | _struct_ = sdk.DiscordInputMode 256 | _fields_ = [ 257 | ("type", InputModeType), 258 | ("shortcut", str), 259 | ] 260 | 261 | type: InputModeType 262 | shortcut: str 263 | 264 | 265 | class FileStat(Model): 266 | _struct_ = sdk.DiscordFileStat 267 | _fields_ = [ 268 | ("filename", str), 269 | ("size", int), 270 | ("last_modified", int), 271 | ] 272 | 273 | filename: str 274 | size: int 275 | last_modified: int 276 | 277 | 278 | class UserAchievement(Model): 279 | _struct_ = sdk.DiscordUserAchievement 280 | _fields_ = [ 281 | ("user_id", str), 282 | ("achievement_id", int), 283 | ("percent_complete", int), 284 | ("unlocked_at", str), 285 | ] 286 | 287 | user_id: str 288 | achievement_id: int 289 | percent_complete: int 290 | unlocked_at: str 291 | 292 | 293 | class SkuPrice(Model): 294 | _struct_ = sdk.DiscordSkuPrice 295 | _fields_ = [ 296 | ("amount", int), 297 | ("currency", str), 298 | ] 299 | 300 | amount: int 301 | currency: str 302 | 303 | 304 | class Sku(Model): 305 | _struct_ = sdk.DiscordSku 306 | _fields_ = [ 307 | ("id", int), 308 | ("type", SkuType), 309 | ("name", str), 310 | ("price", SkuPrice), 311 | ] 312 | 313 | id: int 314 | type: SkuType 315 | name: str 316 | price: SkuPrice 317 | 318 | 319 | class Entitlement(Model): 320 | _struct_ = sdk.DiscordEntitlement 321 | _fields_ = [ 322 | ("id", int), 323 | ("type", EntitlementType), 324 | ("sku_id", int), 325 | ] 326 | 327 | id: int 328 | type: EntitlementType 329 | sku_id: int 330 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/network.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import typing as t 3 | 4 | from . import sdk 5 | from .enum import Result 6 | from .event import bind_events 7 | from .exception import get_exception 8 | 9 | 10 | class NetworkManager: 11 | _internal: sdk.IDiscordNetworkManager = None 12 | _garbage: t.List[t.Any] 13 | _events: sdk.IDiscordNetworkEvents 14 | 15 | def __init__(self): 16 | self._events = bind_events( 17 | sdk.IDiscordNetworkEvents, 18 | self._on_message, 19 | self._on_route_update 20 | ) 21 | 22 | def _on_message(self, event_data, peer_id, channel_id, data, data_length): 23 | data = bytes(data[:data_length]) 24 | self.on_message(peer_id, channel_id, data) 25 | 26 | def _on_route_update(self, event_data, route_data): 27 | self.on_route_update(route_data.decode("utf8")) 28 | 29 | def get_peer_id(self) -> int: 30 | """ 31 | Get the networking peer_id for the current user, allowing other users to send packets to 32 | them. 33 | """ 34 | peerId = sdk.DiscordNetworkPeerId() 35 | self._internal.get_peer_id(self._internal, peerId) 36 | return peerId.value 37 | 38 | def flush(self) -> None: 39 | """ 40 | Flushes the network. 41 | """ 42 | result = Result(self._internal.flush(self._internal)) 43 | if result != Result.ok: 44 | raise get_exception(result) 45 | 46 | def open_channel(self, peer_id: int, channel_id: int, reliable: bool) -> None: 47 | """ 48 | Opens a channel to a user with their given peer_id on the given channel number. 49 | """ 50 | result = Result(self._internal.open_channel(self._internal, peer_id, channel_id, reliable)) 51 | if result != Result.ok: 52 | raise get_exception(result) 53 | 54 | def open_peer(self, peer_id: int, route: str) -> None: 55 | """ 56 | Opens a network connection to another Discord user. 57 | """ 58 | route_data = ctypes.create_string_buffer(route.encode("utf8")) 59 | result = Result(self._internal.open_peer(self._internal, peer_id, route_data)) 60 | if result != Result.ok: 61 | raise get_exception(result) 62 | 63 | def update_peer(self, peer_id: int, route: str) -> None: 64 | """ 65 | Updates the network connection to another Discord user. 66 | """ 67 | route_data = ctypes.create_string_buffer(route.encode("utf8")) 68 | result = Result(self._internal.update_peer(self._internal, peer_id, route_data)) 69 | if result != Result.ok: 70 | raise get_exception(result) 71 | 72 | def send_message(self, peer_id: int, channel_id: int, data: bytes) -> None: 73 | """ 74 | Sends data to a given peer_id through the given channel. 75 | """ 76 | data = (ctypes.c_uint8 * len(data))(*data) 77 | result = Result(self._internal.send_message( 78 | self._internal, 79 | peer_id, 80 | channel_id, 81 | data, 82 | len(data) 83 | )) 84 | if result != Result.ok: 85 | raise get_exception(result) 86 | 87 | def close_channel(self, peer_id: int, channel_id: int) -> None: 88 | """ 89 | Close the connection to a given user by peer_id on the given channel. 90 | """ 91 | result = Result(self._internal.close_channel(self._internal, peer_id, channel_id)) 92 | if result != Result.ok: 93 | raise get_exception(result) 94 | 95 | def close_peer(self, peer_id: int) -> None: 96 | """ 97 | Disconnects the network session to another Discord user. 98 | """ 99 | result = Result(self._internal.close_peer(self._internal, peer_id)) 100 | if result != Result.ok: 101 | raise get_exception(result) 102 | 103 | def on_message(self, peer_id: int, channel_id: int, data: bytes) -> None: 104 | """ 105 | Fires when you receive data from another user. 106 | """ 107 | 108 | def on_route_update(self, route: str) -> None: 109 | """ 110 | Fires when your networking route has changed. 111 | """ 112 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/overlay.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import typing as t 3 | 4 | from . import sdk 5 | from .enum import ActivityActionType, Result 6 | from .event import bind_events 7 | 8 | 9 | class OverlayManager: 10 | _internal: sdk.IDiscordOverlayManager = None 11 | _garbage: t.List[t.Any] 12 | _events: sdk.IDiscordOverlayEvents 13 | 14 | def __init__(self): 15 | self._garbage = [] 16 | self._events = bind_events( 17 | sdk.IDiscordOverlayEvents, 18 | self._on_toggle 19 | ) 20 | 21 | def _on_toggle(self, event_data, locked): 22 | self.on_toggle(locked) 23 | 24 | def is_enabled(self) -> bool: 25 | """ 26 | Check whether the user has the overlay enabled or disabled. 27 | """ 28 | enabled = ctypes.c_bool() 29 | self._internal.is_enabled(self._internal, enabled) 30 | return enabled.value 31 | 32 | def is_locked(self) -> bool: 33 | """ 34 | Check if the overlay is currently locked or unlocked 35 | """ 36 | locked = ctypes.c_bool() 37 | self._internal.is_locked(self._internal, locked) 38 | return locked.value 39 | 40 | def set_locked(self, locked: bool, callback: t.Callable[[Result], None]) -> None: 41 | """ 42 | Locks or unlocks input in the overlay. 43 | """ 44 | def c_callback(event_data, result): 45 | self._garbage.remove(c_callback) 46 | result = Result(result) 47 | callback(result) 48 | 49 | c_callback = self._internal.set_locked.argtypes[-1](c_callback) 50 | self._garbage.append(c_callback) # prevent it from being garbage collected 51 | 52 | self._internal.set_locked(self._internal, locked, ctypes.c_void_p(), c_callback) 53 | 54 | def open_activity_invite( 55 | self, 56 | type: ActivityActionType, 57 | callback: t.Callable[[Result], None] 58 | ) -> None: 59 | """ 60 | Opens the overlay modal for sending game invitations to users, channels, and servers. 61 | """ 62 | def c_callback(event_data, result): 63 | self._garbage.remove(c_callback) 64 | result = Result(result) 65 | callback(result) 66 | 67 | c_callback = self._internal.open_activity_invite.argtypes[-1](c_callback) 68 | self._garbage.append(c_callback) # prevent it from being garbage collected 69 | 70 | self._internal.open_activity_invite(self._internal, type, ctypes.c_void_p(), c_callback) 71 | 72 | def open_guild_invite(self, code: str, callback: t.Callable[[Result], None]) -> None: 73 | """ 74 | Opens the overlay modal for joining a Discord guild, given its invite code. 75 | """ 76 | def c_callback(event_data, result): 77 | self._garbage.remove(c_callback) 78 | result = Result(result) 79 | callback(result) 80 | 81 | c_callback = self._internal.open_guild_invite.argtypes[-1](c_callback) 82 | self._garbage.append(c_callback) # prevent it from being garbage collected 83 | 84 | code = ctypes.c_char_p(code.encode("utf8")) 85 | self._internal.open_guild_invite(self._internal, code, ctypes.c_void_p(), c_callback) 86 | 87 | def open_voice_settings(self, callback: t.Callable[[Result], None]) -> None: 88 | """ 89 | Opens the overlay widget for voice settings for the currently connected application. 90 | """ 91 | def c_callback(event_data, result): 92 | self._garbage.remove(c_callback) 93 | result = Result(result) 94 | callback(result) 95 | 96 | c_callback = self._internal.open_voice_settings.argtypes[-1](c_callback) 97 | self._garbage.append(c_callback) # prevent it from being garbage collected 98 | 99 | self._internal.open_voice_settings(self._internal, ctypes.c_void_p(), c_callback) 100 | 101 | def on_toggle(self, locked: bool) -> None: 102 | """ 103 | Fires when the overlay is locked or unlocked (a.k.a. opened or closed) 104 | """ 105 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/relationship.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import typing as t 3 | 4 | from . import sdk 5 | from .enum import Result 6 | from .event import bind_events 7 | from .exception import get_exception 8 | from .model import Relationship 9 | 10 | 11 | class RelationshipManager: 12 | _internal: sdk.IDiscordRelationshipManager = None 13 | _garbage: t.List[t.Any] 14 | _events: sdk.IDiscordRelationshipEvents 15 | 16 | def __init__(self): 17 | self._garbage = [] 18 | self._events = bind_events( 19 | sdk.IDiscordRelationshipEvents, 20 | self._on_refresh, 21 | self._on_relationship_update 22 | ) 23 | 24 | def _on_refresh(self, event_data): 25 | self.on_refresh() 26 | 27 | def _on_relationship_update(self, event_data, relationship): 28 | self.on_relationship_update(Relationship(copy=relationship.contents)) 29 | 30 | def filter(self, filter: t.Callable[[Relationship], None]) -> None: 31 | """ 32 | Filters a user's relationship list by a boolean condition. 33 | """ 34 | def c_filter(filter_data, relationship): 35 | return bool(filter(Relationship(copy=relationship.contents))) 36 | 37 | c_filter = self._internal.filter.argtypes[-1](c_filter) 38 | 39 | self._internal.filter(self._internal, ctypes.c_void_p(), c_filter) 40 | 41 | def get(self, user_id: int) -> Relationship: 42 | """ 43 | Get the relationship between the current user and a given user by id. 44 | """ 45 | pointer = sdk.DiscordRelationship() 46 | result = Result(self._internal.get(self._internal, user_id, pointer)) 47 | if result != Result.ok: 48 | raise get_exception(result) 49 | 50 | return Relationship(internal=pointer) 51 | 52 | def get_at(self, index: int) -> Relationship: 53 | """ 54 | Get the relationship at a given index when iterating over a list of relationships. 55 | """ 56 | pointer = sdk.DiscordRelationship() 57 | result = Result(self._internal.get_at(self._internal, index, pointer)) 58 | if result != Result.ok: 59 | raise get_exception(result) 60 | 61 | return Relationship(internal=pointer) 62 | 63 | def count(self) -> int: 64 | """ 65 | Get the number of relationships that match your filter. 66 | """ 67 | count = ctypes.c_int32() 68 | result = Result(self._internal.count(self._internal, count)) 69 | if result != Result.ok: 70 | raise get_exception(result) 71 | 72 | return count.value 73 | 74 | def on_refresh(self) -> None: 75 | """ 76 | Fires at initialization when Discord has cached a snapshot of the current status of all 77 | your relationships. 78 | """ 79 | 80 | def on_relationship_update(self, relationship: Relationship) -> None: 81 | """ 82 | Fires when a relationship in the filtered list changes, like an updated presence or user 83 | attribute. 84 | """ 85 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/storage.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import typing as t 3 | 4 | from . import sdk 5 | from .enum import Result 6 | from .exception import get_exception 7 | from .model import FileStat 8 | 9 | 10 | class StorageManager: 11 | _internal: sdk.IDiscordStorageManager = None 12 | _garbage: t.List[t.Any] 13 | _events: sdk.IDiscordStorageEvents = None 14 | 15 | def __init__(self): 16 | self._garbage = [] 17 | 18 | def get_path(self) -> str: 19 | """ 20 | Returns the filepath to which Discord saves files if you were to use the SDK's storage 21 | manager. 22 | """ 23 | path = sdk.DiscordPath() 24 | 25 | result = Result(self._internal.get_path(self._internal, path)) 26 | if result != Result.ok: 27 | raise get_exception(result) 28 | 29 | return path.value.decode("utf8") 30 | 31 | def read(self, name: str) -> bytes: 32 | """ 33 | Reads data synchronously from the game's allocated save file. 34 | """ 35 | # we need the file stat for this one, as length-fixed buffers does not exist in python 36 | file_stat = self.stat(name) 37 | file_size = file_stat.Size 38 | 39 | name = ctypes.c_char_p(name.encode("utf8")) 40 | buffer = (ctypes.c_uint8 * file_size)() 41 | read = ctypes.c_uint32() 42 | 43 | result = Result(self._internal.read(self._internal, name, buffer, len(buffer), read)) 44 | if result != Result.ok: 45 | raise get_exception(result) 46 | 47 | if read.value != file_size: 48 | print("discord/storage.py: warning: attempting to read " + 49 | str(file_size) + " bytes, but read " + str(read.value)) 50 | 51 | return bytes(buffer[:read.value]) 52 | 53 | def read_async( 54 | self, 55 | name: str, 56 | callback: t.Callable[[Result, t.Optional[bytes]], None] 57 | ) -> None: 58 | """ 59 | Reads data asynchronously from the game's allocated save file. 60 | 61 | Returns discordsdk.enum.Result (int) and data (bytes) via callback. 62 | """ 63 | def c_callback(callback_data, result, data, data_length): 64 | self._garbage.remove(c_callback) 65 | result = Result(result) 66 | if result == Result.ok: 67 | data = bytes(data[:data_length]) 68 | callback(result, data) 69 | else: 70 | callback(result, None) 71 | 72 | c_callback = self._internal.read_async.argtypes[-1](c_callback) 73 | self._garbage.append(c_callback) # prevent it from being garbage collected 74 | 75 | name = ctypes.c_char_p(name.encode("utf8")) 76 | self._internal.read_async(self._internal, name, ctypes.c_void_p(), c_callback) 77 | 78 | def read_async_partial( 79 | self, 80 | name: str, 81 | offset: int, 82 | length: int, 83 | callback: t.Callable[[Result], None] 84 | ) -> None: 85 | """ 86 | Reads data asynchronously from the game's allocated save file, starting at a given offset 87 | and up to a given length. 88 | """ 89 | def c_callback(callback_data, result, data, data_length): 90 | self._garbage.remove(c_callback) 91 | result = Result(result) 92 | if result == Result.ok: 93 | data = bytes(data[:data_length]) 94 | callback(result, data) 95 | else: 96 | callback(result, None) 97 | 98 | c_callback = self._internal.read_async.argtypes[-1](c_callback) 99 | self._garbage.append(c_callback) # prevent it from being garbage collected 100 | 101 | name = ctypes.c_char_p(name.encode("utf8")) 102 | self._internal.read_async_partial( 103 | self._internal, 104 | name, 105 | offset, 106 | length, 107 | ctypes.c_void_p(), 108 | c_callback 109 | ) 110 | 111 | def write(self, name: str, data: bytes) -> None: 112 | """ 113 | Writes data synchronously to disk, under the given key name. 114 | """ 115 | name = ctypes.c_char_p(name.encode("utf8")) 116 | data = (ctypes.c_uint8 * len(data))(*data) 117 | 118 | result = Result(self._internal.write(self._internal, name, data, len(data))) 119 | if result != Result.ok: 120 | raise get_exception(result) 121 | 122 | def write_async(self, name: str, data: bytes, callback: t.Callable[[Result], None]) -> None: 123 | """ 124 | Writes data asynchronously to disk under the given keyname. 125 | """ 126 | def c_callback(callback_data, result): 127 | self._garbage.remove(c_callback) 128 | result = Result(result) 129 | callback(result) 130 | 131 | c_callback = self._internal.write_async.argtypes[-1](c_callback) 132 | self._garbage.append(c_callback) # prevent it from being garbage collected 133 | 134 | name = ctypes.c_char_p(name.encode("utf8")) 135 | data = (ctypes.c_uint8 * len(data))(*data) 136 | 137 | self._internal.write_async( 138 | self._internal, 139 | name, 140 | data, 141 | len(data), 142 | ctypes.c_void_p(), 143 | c_callback 144 | ) 145 | 146 | def delete(self, name: str) -> None: 147 | """ 148 | Deletes written data for the given key name. 149 | """ 150 | name = ctypes.c_char_p(name.encode("utf8")) 151 | 152 | result = Result(self._internal.delete_(self._internal, name)) 153 | if result != Result.ok: 154 | raise get_exception(result) 155 | 156 | def exists(self, name: str) -> bool: 157 | """ 158 | Checks if data exists for a given key name. 159 | """ 160 | exists = ctypes.c_bool() 161 | name = ctypes.c_char_p(name.encode("utf8")) 162 | 163 | result = Result(self._internal.exists(self._internal, name, exists)) 164 | if result != Result.ok: 165 | raise get_exception(result) 166 | 167 | return exists.value 168 | 169 | def stat(self, name: str) -> FileStat: 170 | """ 171 | Returns file info for the given key name. 172 | """ 173 | stat = sdk.DiscordFileStat() 174 | 175 | name = ctypes.c_char_p(name.encode("utf8")) 176 | result = Result(self._internal.stat(self._internal, name, stat)) 177 | if result != Result.ok: 178 | raise get_exception(result) 179 | 180 | return FileStat(internal=stat) 181 | 182 | def count(self) -> int: 183 | """ 184 | Returns the count of files, for iteration. 185 | """ 186 | count = ctypes.c_int32() 187 | self._internal.count(self._internal, count) 188 | return count.value 189 | 190 | def stat_at(self, index: int) -> FileStat: 191 | """ 192 | Returns file info for the given index when iterating over files. 193 | """ 194 | stat = sdk.DiscordFileStat() 195 | 196 | result = Result(self._internal.stat_at(self._internal, index, stat)) 197 | if result != Result.ok: 198 | raise get_exception(result) 199 | 200 | return FileStat(internal=stat) 201 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/store.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import typing as t 3 | 4 | from . import sdk 5 | from .enum import Result 6 | from .event import bind_events 7 | from .exception import get_exception 8 | from .model import Entitlement, Sku 9 | 10 | 11 | class StoreManager: 12 | _internal: sdk.IDiscordStoreManager = None 13 | _garbage: t.List[t.Any] 14 | _events: sdk.IDiscordStoreEvents 15 | 16 | def __init__(self): 17 | self._garbage = [] 18 | self._events = bind_events( 19 | sdk.IDiscordStoreEvents, 20 | self._on_entitlement_create, 21 | self._on_entitlement_delete 22 | ) 23 | 24 | def _on_entitlement_create(self, event_data, entitlement): 25 | self.on_entitlement_create(Entitlement(copy=entitlement)) 26 | 27 | def _on_entitlement_delete(self, event_data, entitlement): 28 | self.on_entitlement_delete(Entitlement(copy=entitlement)) 29 | 30 | def fetch_skus(self, callback: t.Callable[[Result], None]) -> None: 31 | """ 32 | Fetches the list of SKUs for the connected application, readying them for iteration. 33 | 34 | Returns discordsdk.enum.Result (int) via callback. 35 | """ 36 | def c_callback(callback_data, result): 37 | self._garbage.remove(c_callback) 38 | result = Result(result) 39 | callback(result) 40 | 41 | c_callback = self._internal.fetch_skus.argtypes[-1](c_callback) 42 | self._garbage.append(c_callback) # prevent it from being garbage collected 43 | 44 | self._internal.fetch_skus(self._internal, ctypes.c_void_p(), c_callback) 45 | 46 | def count_skus(self) -> int: 47 | """ 48 | Get the number of SKUs readied by FetchSkus(). 49 | """ 50 | count = ctypes.c_int32() 51 | self._internal.count_skus(self._internal, count) 52 | return count.value 53 | 54 | def get_sku(self, sku_id: int) -> Sku: 55 | """ 56 | Gets a SKU by its ID. 57 | """ 58 | sku = sdk.DiscordSku() 59 | 60 | result = Result(self._internal.get_sku(sku_id, sku)) 61 | if result != Result.ok: 62 | raise get_exception(result) 63 | 64 | return Sku(internal=sku) 65 | 66 | def get_sku_at(self, index: int) -> Sku: 67 | """ 68 | Gets a SKU by index when iterating over SKUs. 69 | """ 70 | sku = sdk.DiscordSku() 71 | 72 | result = Result(self._internal.get_sku_at(index, sku)) 73 | if result != Result.ok: 74 | raise get_exception(result) 75 | 76 | return Sku(internal=sku) 77 | 78 | def fetch_entitlements(self, callback: t.Callable[[Result], None]) -> None: 79 | """ 80 | Fetches a list of entitlements to which the user is entitled. 81 | 82 | Returns discordsdk.enum.Result (int) via callback. 83 | """ 84 | def c_callback(callback_data, result): 85 | self._garbage.remove(c_callback) 86 | result = Result(result) 87 | callback(result) 88 | 89 | c_callback = self._internal.fetch_entitlements.argtypes[-1](c_callback) 90 | self._garbage.append(c_callback) # prevent it from being garbage collected 91 | 92 | self._internal.fetch_entitlements(self._internal, ctypes.c_void_p(), c_callback) 93 | 94 | def count_entitlements(self) -> int: 95 | """ 96 | Get the number of entitlements readied by FetchEntitlements(). 97 | """ 98 | count = ctypes.c_int32() 99 | self._internal.count_entitlements(self._internal, count) 100 | return count.value 101 | 102 | def get_entitlement(self, entitlement_id: int) -> Entitlement: 103 | """ 104 | Gets an entitlement by its id. 105 | """ 106 | entitlement = sdk.DiscordEntitlement() 107 | 108 | result = Result(self._internal.get_entitlement(entitlement_id, entitlement)) 109 | if result != Result.ok: 110 | raise get_exception(result) 111 | 112 | return Entitlement(internal=Sku) 113 | 114 | def get_entitlement_at(self, index: int) -> Entitlement: 115 | """ 116 | Gets an entitlement by index when iterating over a user's entitlements. 117 | """ 118 | entitlement = sdk.DiscordEntitlement() 119 | 120 | result = Result(self._internal.get_entitlement_at(index, entitlement)) 121 | if result != Result.ok: 122 | raise get_exception(result) 123 | 124 | return Entitlement(internal=Sku) 125 | 126 | def has_sku_entitlement(self, sku_id: int) -> bool: 127 | """ 128 | Returns whether or not the user is entitled to the given SKU ID. 129 | """ 130 | has_entitlement = ctypes.c_bool() 131 | 132 | result = Result(self._internal.has_sku_entitlement(sku_id, has_entitlement)) 133 | if result != Result.ok: 134 | raise get_exception(result) 135 | 136 | return has_entitlement.value 137 | 138 | def start_purchase(self, sku_id: int, callback: t.Callable[[Result], None]) -> None: 139 | """ 140 | Opens the overlay to begin the in-app purchase dialogue for the given SKU ID. 141 | 142 | Returns discordsdk.enum.Result (int) via callback. 143 | """ 144 | def c_callback(callback_data, result): 145 | self._garbage.remove(c_callback) 146 | result = Result(result) 147 | callback(result) 148 | 149 | c_callback = self._internal.start_purchase.argtypes[-1](c_callback) 150 | self._garbage.append(c_callback) # prevent it from being garbage collected 151 | 152 | self._internal.start_purchase(self._internal, sku_id, ctypes.c_void_p(), c_callback) 153 | 154 | def on_entitlement_create(self, entitlement: Entitlement) -> None: 155 | """ 156 | Fires when the connected user receives a new entitlement, either through purchase or 157 | through a developer grant. 158 | """ 159 | 160 | def on_entitlement_delete(self, entitlement: Entitlement) -> None: 161 | """ 162 | Fires when the connected user loses an entitlement, either by expiration, revocation, or 163 | consumption in the case of consumable entitlements. 164 | """ 165 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/user.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import typing as t 3 | 4 | from . import sdk 5 | from .enum import PremiumType, Result, UserFlag 6 | from .event import bind_events 7 | from .exception import get_exception 8 | from .model import User 9 | 10 | 11 | class UserManager: 12 | _internal: sdk.IDiscordUserManager = None 13 | _garbage: t.List[t.Any] 14 | _events: sdk.IDiscordUserEvents 15 | 16 | def __init__(self): 17 | self._garbage = [] 18 | self._events = bind_events( 19 | sdk.IDiscordUserEvents, 20 | self._on_current_user_update 21 | ) 22 | 23 | def _on_current_user_update(self, event_data): 24 | self.on_current_user_update() 25 | 26 | def get_current_user(self) -> User: 27 | """ 28 | Fetch information about the currently connected user account. 29 | """ 30 | user = sdk.DiscordUser() 31 | result = Result(self._internal.get_current_user(self._internal, user)) 32 | if result != Result.ok: 33 | raise get_exception(result) 34 | 35 | return User(internal=user) 36 | 37 | def get_user(self, user_id: int, callback: t.Callable[[Result], None]) -> None: 38 | """ 39 | Get user information for a given id. 40 | 41 | Returns discordsdk.enum.Result (int) and User via callback. 42 | """ 43 | def c_callback(callback_data, result, user): 44 | self._garbage.remove(c_callback) 45 | result = Result(result) 46 | if result == Result.ok: 47 | callback(result, User(copy=user.contents)) 48 | else: 49 | callback(result, None) 50 | 51 | c_callback = self._internal.get_user.argtypes[-1](c_callback) 52 | self._garbage.append(c_callback) # prevent it from being garbage collected 53 | 54 | self._internal.get_user(self._internal, user_id, ctypes.c_void_p(), c_callback) 55 | 56 | def get_current_user_premium_type(self) -> PremiumType: 57 | """ 58 | Get the PremiumType for the currently connected user. 59 | """ 60 | premium_type = ctypes.c_int32() 61 | result = Result(self._internal.get_current_user_premium_type(self._internal, premium_type)) 62 | if result != Result.ok: 63 | raise get_exception(result) 64 | 65 | return PremiumType(premium_type.value) 66 | 67 | def current_user_has_flag(self, flag: UserFlag) -> bool: 68 | """ 69 | See whether or not the current user has a certain UserFlag on their account. 70 | """ 71 | has_flag = ctypes.c_bool() 72 | result = Result(self._internal.current_user_has_flag(self._internal, flag, has_flag)) 73 | if result != Result.ok: 74 | raise get_exception(result) 75 | 76 | return has_flag.value 77 | 78 | def on_current_user_update(self) -> None: 79 | """ 80 | Fires when the User struct of the currently connected user changes. 81 | """ 82 | -------------------------------------------------------------------------------- /gw2rpc/lib/discordsdk/voice.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import typing as t 3 | 4 | from . import sdk 5 | from .enum import Result 6 | from .event import bind_events 7 | from .exception import get_exception 8 | from .model import InputMode 9 | 10 | 11 | class VoiceManager: 12 | _internal: sdk.IDiscordVoiceManager = None 13 | _garbage: t.List[t.Any] 14 | _events: sdk.IDiscordVoiceEvents 15 | 16 | def __init__(self): 17 | self._garbage = [] 18 | self._events = bind_events( 19 | sdk.IDiscordVoiceEvents, 20 | self._on_settings_update 21 | ) 22 | 23 | def _on_settings_update(self, event_data): 24 | self.on_settings_update() 25 | 26 | def get_input_mode(self) -> InputMode: 27 | """ 28 | Get the current voice input mode for the user 29 | """ 30 | input_mode = sdk.DiscordInputMode() 31 | result = Result(self._internal.get_input_mode(self._internal, input_mode)) 32 | if result != Result.ok: 33 | raise get_exception(result) 34 | 35 | return InputMode(internal=input_mode) 36 | 37 | def set_input_mode(self, inputMode: InputMode, callback: t.Callable[[Result], None]) -> None: 38 | """ 39 | Sets a new voice input mode for the uesr. 40 | 41 | Returns discordsdk.enum.Result (int) via callback. 42 | """ 43 | def c_callback(callback_data, result): 44 | self._garbage.remove(c_callback) 45 | result = Result(result) 46 | callback(result) 47 | 48 | c_callback = self._internal.set_input_mode.argtypes[-1](c_callback) 49 | self._garbage.append(c_callback) # prevent it from being garbage collected 50 | 51 | self._internal.set_input_mode( 52 | self._internal, 53 | inputMode._internal, 54 | ctypes.c_void_p(), 55 | c_callback 56 | ) 57 | 58 | def is_self_mute(self) -> bool: 59 | """ 60 | Whether the connected user is currently muted. 61 | """ 62 | mute = ctypes.c_bool() 63 | result = Result(self._internal.is_self_mute(self._internal, mute)) 64 | if result != Result.ok: 65 | raise get_exception(result) 66 | 67 | return mute.value 68 | 69 | def set_self_mute(self, mute: bool) -> None: 70 | """ 71 | Mutes or unmutes the currently connected user. 72 | """ 73 | result = Result(self._internal.set_self_mute(self._internal, mute)) 74 | if result != Result.ok: 75 | raise get_exception(result) 76 | 77 | def is_self_deaf(self) -> bool: 78 | """ 79 | Whether the connected user is currently deafened. 80 | """ 81 | deaf = ctypes.c_bool() 82 | result = Result(self._internal.is_self_deaf(self._internal, deaf)) 83 | if result != Result.ok: 84 | raise get_exception(result) 85 | 86 | return deaf.value 87 | 88 | def set_self_deaf(self, deaf: bool) -> None: 89 | """ 90 | Deafens or undefeans the currently connected user. 91 | """ 92 | result = Result(self._internal.set_self_deaf(self._internal, deaf)) 93 | if result != Result.ok: 94 | raise get_exception(result) 95 | 96 | def is_local_mute(self, user_id: int) -> bool: 97 | """ 98 | Whether the given user is currently muted by the connected user. 99 | """ 100 | mute = ctypes.c_bool() 101 | result = Result(self._internal.is_local_mute(self._internal, user_id, mute)) 102 | if result != Result.ok: 103 | raise get_exception(result) 104 | 105 | return mute.value 106 | 107 | def set_local_mute(self, user_id: int, mute: bool) -> None: 108 | """ 109 | Mutes or unmutes the given user for the currently connected user. 110 | """ 111 | result = Result(self._internal.set_local_mute(self._internal, user_id, mute)) 112 | if result != Result.ok: 113 | raise get_exception(result) 114 | 115 | def get_local_volume(self, user_id: int) -> int: 116 | """ 117 | Gets the local volume for a given user. 118 | """ 119 | volume = ctypes.c_uint8() 120 | result = Result(self._internal.get_local_volume(self._internal, user_id, volume)) 121 | if result != Result.ok: 122 | raise get_exception(result) 123 | 124 | return volume.value 125 | 126 | def set_local_volume(self, user_id: int, volume: int) -> None: 127 | """ 128 | Sets the local volume for a given user. 129 | """ 130 | result = Result(self._internal.set_local_volume(self._internal, user_id, volume)) 131 | if result != Result.ok: 132 | raise get_exception(result) 133 | 134 | def on_settings_update(self) -> None: 135 | # This event is not documented anywhere (yet?) 136 | pass 137 | -------------------------------------------------------------------------------- /gw2rpc/mumble.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import json 3 | from json.decoder import JSONDecodeError 4 | import mmap 5 | import time 6 | import socket 7 | 8 | class MumbleLinkException(Exception): 9 | pass 10 | 11 | class Context(ctypes.Structure): 12 | _fields_ = [ 13 | ("serverAddress", ctypes.c_ubyte * 28), # 28 bytes 14 | ("mapId", ctypes.c_uint32), # 4 bytes 15 | ("mapType", ctypes.c_uint32), # 4 bytes 16 | ("shardId", ctypes.c_uint32), # 4 bytes 17 | ("instance", ctypes.c_uint32), # 4 bytes 18 | ("buildId", ctypes.c_uint32), # 4 bytes 19 | ("uiState", ctypes.c_uint32), # 4 bytes 20 | ("compassWidth", ctypes.c_uint16), # 2 bytes 21 | ("compassHeight", ctypes.c_uint16), # 2 bytes 22 | ("compassRotation", ctypes.c_float), # 4 bytes 23 | ("playerX", ctypes.c_float), # 4 bytes 24 | ("playerY", ctypes.c_float), # 4 bytes 25 | ("mapCenterX", ctypes.c_float), # 4 bytes 26 | ("mapCenterY", ctypes.c_float), # 4 bytes 27 | ("mapScale", ctypes.c_float), # 4 bytes 28 | ("processId", ctypes.c_uint32), # 4 bytes 29 | ("mountIndex", ctypes.c_uint8), # 1 byte 30 | ] 31 | 32 | 33 | # yapf:disable QA OFF 34 | class Link(ctypes.Structure): 35 | _fields_ = [ 36 | ("uiVersion", ctypes.c_uint32), # 4 bytes 37 | ("uiTick", ctypes.c_ulong), # 4 bytes 38 | ("fAvatarPosition", ctypes.c_float * 3), # 3*4 bytes 39 | ("fAvatarFront", ctypes.c_float * 3), # 3*4 bytes 40 | ("fAvatarTop", ctypes.c_float * 3), # 3*4 bytes 41 | ("name", ctypes.c_wchar * 256), # 512 bytes 42 | ("fCameraPosition", ctypes.c_float * 3), # 3*4 bytes 43 | ("fCameraFront", ctypes.c_float * 3), # 3*4 bytes 44 | ("fCameraTop", ctypes.c_float * 3), # 3*4 bytes 45 | ("identity", ctypes.c_wchar * 256), # 512 bytes 46 | ("context_len", ctypes.c_uint32), # 4 bytes 47 | # ("context", ctypes.c_ubyte * 256), # 256 bytes, see below 48 | # ("description", ctypes.c_wchar * 2048), # 4096 bytes, always empty 49 | ] 50 | # yapf:enable QA ON 51 | 52 | 53 | class MumbleData: 54 | def __init__(self, mumble_link="MumbleLink"): 55 | self.mumble_link = mumble_link 56 | self.memfile = None 57 | self.last_map_id = None 58 | self.last_timestamp = None 59 | self.last_character_name = None 60 | self.size_link = ctypes.sizeof(Link) 61 | self.size_context = ctypes.sizeof(Context) 62 | self.in_focus = False 63 | self.in_combat = False 64 | self.last_server_ip = None 65 | 66 | def create_map(self): 67 | size_discarded = 256 - self.size_context + 4096 # empty areas of context and description 68 | memfile_length = self.size_link + self.size_context + size_discarded 69 | self.memfile = mmap.mmap(-1, memfile_length, self.mumble_link) 70 | 71 | def close_map(self): 72 | if self.memfile: 73 | self.memfile.close() 74 | self.memfile = None 75 | self.last_map_id = None 76 | self.last_timestamp = None 77 | self.last_character_name = None 78 | self.in_focus = False 79 | self.in_combat = False 80 | self.last_server_ip = None 81 | 82 | @staticmethod 83 | def Unpack(ctype, buf): 84 | cstring = ctypes.create_string_buffer(buf) 85 | ctype_instance = ctypes.cast( 86 | ctypes.pointer(cstring), ctypes.POINTER(ctype)).contents 87 | return ctype_instance 88 | 89 | def get_mumble_data(self, process=None): 90 | self.memfile.seek(0) 91 | data = self.memfile.read(self.size_link) 92 | context = self.memfile.read(self.size_context) 93 | result = self.Unpack(Link, data) 94 | result_context = self.Unpack(Context, context) 95 | if not result.identity: 96 | return None 97 | try: 98 | data = json.loads(result.identity) 99 | except JSONDecodeError: 100 | return None 101 | 102 | uiState = result_context.uiState 103 | self.in_focus = bool(uiState & 0b1000) 104 | self.in_combat = bool(uiState & 0b1000000) 105 | 106 | # Check if in character selection or ingame 107 | address_family = result_context.serverAddress[0] 108 | if address_family == socket.AF_INET: 109 | self.last_server_ip = socket.inet_ntop(socket.AF_INET, bytearray(result_context.serverAddress[4:8])) 110 | elif address_family == socket.AF_INET6: 111 | # NOT implemented, because format of IPv6 Server address in Context struct is not documented! 112 | self.last_server_ip = None 113 | else: 114 | self.last_server_ip = None 115 | 116 | if process and self.last_server_ip: 117 | try: 118 | for conn in process.connections(): 119 | if conn.status == 'ESTABLISHED' and conn.raddr.ip == self.last_server_ip: 120 | break 121 | else: 122 | return None 123 | except: 124 | pass 125 | 126 | data["mount_index"] = result_context.mountIndex 127 | data["in_combat"] = self.in_combat 128 | character = data["name"] 129 | map_id = data["map_id"] 130 | if self.last_character_name != character or self.last_map_id != map_id: 131 | self.last_timestamp = int(time.time()) 132 | self.last_map_id = map_id 133 | self.last_character_name = character 134 | return data 135 | 136 | def get_position(self): 137 | self.memfile.seek(0) 138 | data = self.memfile.read(self.size_link) 139 | result = self.Unpack(Link, data) 140 | return Position(result.fAvatarPosition) 141 | 142 | 143 | class Position: 144 | def __init__(self, position_data): 145 | def m_to_in(m): 146 | return m * 39.3700787 147 | 148 | self.x = m_to_in(position_data[0]) 149 | self.y = m_to_in(position_data[2]) 150 | self.z = position_data[1] 151 | -------------------------------------------------------------------------------- /gw2rpc/rpc.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is a modified version of GiovanniMCMXCIX's PyDiscordRPC 3 | https://github.com/GiovanniMCMXCIX/PyDiscordRPC 4 | 5 | MIT License 6 | 7 | Copyright (c) 2017 GiovanniMCMXCIX 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | """ 27 | 28 | 29 | import asyncio 30 | import json 31 | import struct 32 | import time 33 | import logging 34 | 35 | log = logging.getLogger(__name__) 36 | 37 | 38 | class DiscordRPC: 39 | def __init__(self, client_id): 40 | self.ipc_path = r'\\?\pipe\discord-ipc-0' 41 | self.loop = asyncio.ProactorEventLoop() 42 | self.sock_reader: asyncio.StreamReader = None 43 | self.sock_writer: asyncio.StreamWriter = None 44 | self.client_id = client_id 45 | self.running = False 46 | self.last_update = time.time() 47 | self.last_payload = {} 48 | self.last_pid = None 49 | 50 | async def read_output(self): 51 | data = await self.sock_reader.read(1024) 52 | code, length = struct.unpack(' None: 12 | self.client_id = client_id 13 | self.start() 14 | self.activity = Activity() 15 | 16 | def start(self): 17 | try: 18 | self.app = Discord(int(self.client_id), CreateFlags.no_require_discord) 19 | self.activity_manager = self.app.get_activity_manager() 20 | except: 21 | self.app = None 22 | self.activity_manager = None 23 | log.debug("Discord not running.") 24 | 25 | def set_activity(self, a): 26 | 27 | def verify_length( val): 28 | if len(val) > 100: 29 | val = val[:97] + "..." 30 | return val 31 | 32 | self.activity.state = verify_length(a["state"]) 33 | self.activity.details = verify_length(a["details"] ) 34 | if a["timestamps"]: 35 | self.activity.timestamps.start = a["timestamps"]["start"] 36 | self.activity.assets.small_image = a["assets"]["small_image"] 37 | self.activity.assets.small_text = verify_length(a["assets"]["small_text"]) 38 | self.activity.assets.large_image = a["assets"]["large_image"] 39 | self.activity.assets.large_text = verify_length(a["assets"]["large_text"]) 40 | 41 | #self.activity.buttons = a["buttons"][0] 42 | 43 | #self.activity.party.id = str(uuid.uuid4()) 44 | #self.activity.secrets.join = str(uuid.uuid4()) 45 | 46 | try: 47 | self.activity_manager.update_activity(self.activity, self.callback) 48 | except OSError: 49 | log.debug("Error reading activity manager.") 50 | pass 51 | 52 | def close(self): 53 | try: 54 | self.app = None 55 | except: 56 | pass 57 | 58 | def callback(self, result): 59 | if result == Result.ok: 60 | log.debug("Successfully set the activity!") 61 | else: 62 | pass 63 | #raise Exception(result) -------------------------------------------------------------------------------- /gw2rpc/settings.py: -------------------------------------------------------------------------------- 1 | import configparser 2 | import os 3 | import logging 4 | from xmlrpc.client import boolean 5 | from enum import Enum 6 | 7 | log = logging.getLogger() 8 | 9 | class Loglevels(Enum): 10 | DEBUG = logging.DEBUG 11 | INFO = logging.INFO 12 | WARNING = logging.WARNING 13 | CRITICAL = logging.CRITICAL 14 | 15 | class Config: 16 | def __init__(self): 17 | def set_boolean(header, setting, fallback=False): 18 | try: 19 | value = self.config.getboolean(header, setting, fallback=fallback) 20 | except ValueError: 21 | value = False 22 | return value 23 | 24 | def set_string(header, setting, default): 25 | try: 26 | value = self.config[header][setting] 27 | except KeyError: 28 | value = default 29 | return value 30 | 31 | supported_languages = ["en", "es", "de", "fr", "pt-br"] 32 | 33 | self.config = configparser.ConfigParser(allow_no_value=True) 34 | if not os.path.exists("config.ini"): 35 | self.config["API"] = {"APIKey": ""} 36 | self.config["Settings"] = { 37 | "CloseWithGw2": False, 38 | "DisplayGuildTag": True, 39 | "Lang": "en", 40 | "HideCommanderTag": False, 41 | "HideMounts": False, 42 | "LogLevel": "info" 43 | } 44 | self.config["PointsOfInterest"] = { 45 | "DisableInWvW": False, 46 | "DisableCompletely": False, 47 | "HidePoiButton": False 48 | } 49 | self.config["Webhooks"] = { 50 | "WebHook": "", 51 | "AnnounceRaid": True, 52 | "DisableInWvW": False 53 | } 54 | with open("config.ini", "w") as cfile: 55 | self.config.write(cfile) 56 | self.config.read("config.ini") 57 | try: 58 | self.api_keys = [ 59 | k for k in map(str.strip, self.config["API"]["APIKey"].split(',')) if k 60 | ] 61 | except KeyError: 62 | self.api_keys = [] 63 | try: 64 | self.webhooks = [ 65 | k for k in map(str.strip, self.config["Webhooks"]["WebHook"].split(',')) if k 66 | ] 67 | except KeyError: 68 | self.webhooks = [] 69 | self.close_with_gw2 = set_boolean("Settings", "CloseWithGw2") 70 | self.display_tag = set_boolean("Settings", "DisplayGuildTag") 71 | self.hide_commander_tag = set_boolean("Settings", "HideCommanderTag") 72 | self.hide_mounts = set_boolean("Settings", "HideMounts") 73 | try: 74 | self.lang = self.config["Settings"]["Lang"] if self.config["Settings"]["Lang"] in supported_languages else "en" 75 | except KeyError: 76 | log.error("Missing language parameter, defaulting to en. Add 'lang = en' for localization support to config.ini.") 77 | self.lang = "en" 78 | try: 79 | log_level = self.config["Settings"]["LogLevel"] if self.config["Settings"]["LogLevel"].lower() in ["debug", "info", "warning", "critical"] else "info" 80 | self.log_level = Loglevels[log_level.upper()].value 81 | except KeyError: 82 | log.error("Missing 'logLevel' Parameter in config.ini. Defaulting to 'info'") 83 | self.log_level = Loglevels["INFO"].value 84 | 85 | self.disable_pois = set_boolean("PointsOfInterest", 86 | "DisableCompletely") 87 | self.disable_pois_in_wvw = set_boolean("PointsOfInterest", 88 | "DisableInWvW") 89 | self.hide_poi_button = set_boolean("PointsOfInterest", 90 | "HidePoiButton") 91 | self.announce_raid = set_boolean("Webhooks", "AnnounceRaid", fallback=True) 92 | self.disable_raid_announce_in_wvw = set_boolean("Webhooks", "DisableInWvW") 93 | 94 | def change_boolean_item(self, section, item, value: boolean): 95 | self.config.set(section, item, str(value)) 96 | try: 97 | with open('config.ini', 'w') as f: 98 | self.config.write(f) 99 | except: 100 | log.error("Could not write to config.ini") 101 | 102 | config = Config() 103 | -------------------------------------------------------------------------------- /icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maselkov/GW2RPC/444d25c36db200aca1955f284e6a502c4e445c49/icon.ico -------------------------------------------------------------------------------- /launch_gw2_with_rpc.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import os.path 3 | import subprocess 4 | 5 | rpc_path = "gw2rpc.exe" 6 | gw2_64_path = "../../Gw2-64.exe" 7 | gw2_path = "../../Gw2.exe" 8 | tasklist = subprocess.check_output(['tasklist'], shell=True) 9 | 10 | 11 | def Mbox(title, text, style): 12 | return ctypes.windll.user32.MessageBoxW(0, text, title, style) 13 | 14 | 15 | def check_gw(): 16 | if os.path.isfile(gw2_64_path): 17 | if b"Gw2-64.exe" not in tasklist: 18 | subprocess.Popen([gw2_64_path]) 19 | print("Started 64") 20 | elif os.path.isfile(gw2_path): 21 | if b"Gw2.exe" not in tasklist: 22 | subprocess.Popen([gw2_path]) 23 | print("Started 32") 24 | else: 25 | Mbox('Error', 'Gw2 not found. Please move the RPC files into ' 26 | 'addons\RPC\ in your GW2 installation folder.', 0) 27 | 28 | 29 | def check_rpc(): 30 | if os.path.isfile(rpc_path): 31 | if b"gw2rpc.exe" not in tasklist: 32 | subprocess.Popen([rpc_path]) 33 | print("Started rpc") 34 | else: 35 | Mbox('Error', 'gw2rpc.exe not found. Please move the RPC files into ' 36 | 'addons\RPC\ in your GW2 installation folder.', 0) 37 | 38 | 39 | if __name__ == "__main__": 40 | check_gw() 41 | check_rpc() 42 | -------------------------------------------------------------------------------- /lib/discord_game_sdk.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maselkov/GW2RPC/444d25c36db200aca1955f284e6a502c4e445c49/lib/discord_game_sdk.dll -------------------------------------------------------------------------------- /locales/base.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR ORGANIZATION 3 | # FIRST AUTHOR , YEAR. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: PACKAGE VERSION\n" 8 | "POT-Creation-Date: 2021-05-28 15:15+0200\n" 9 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 10 | "Last-Translator: FULL NAME \n" 11 | "Language-Team: LANGUAGE \n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Generated-By: pygettext.py 1.5\n" 16 | 17 | 18 | #: gw2rpc/character.py:6 19 | msgid "Guardian" 20 | msgstr "" 21 | 22 | #: gw2rpc/character.py:7 23 | msgid "Warrior" 24 | msgstr "" 25 | 26 | #: gw2rpc/character.py:8 27 | msgid "Engineer" 28 | msgstr "" 29 | 30 | #: gw2rpc/character.py:9 31 | msgid "Ranger" 32 | msgstr "" 33 | 34 | #: gw2rpc/character.py:10 35 | msgid "Thief" 36 | msgstr "" 37 | 38 | #: gw2rpc/character.py:11 39 | msgid "Elementalist" 40 | msgstr "" 41 | 42 | #: gw2rpc/character.py:12 43 | msgid "Mesmer" 44 | msgstr "" 45 | 46 | #: gw2rpc/character.py:13 47 | msgid "Necromancer" 48 | msgstr "" 49 | 50 | #: gw2rpc/character.py:14 51 | msgid "Revenant" 52 | msgstr "" 53 | 54 | #: gw2rpc/character.py:17 55 | msgid "Asura" 56 | msgstr "" 57 | 58 | #: gw2rpc/character.py:17 59 | msgid "Charr" 60 | msgstr "" 61 | 62 | #: gw2rpc/character.py:17 63 | msgid "Human" 64 | msgstr "" 65 | 66 | #: gw2rpc/character.py:17 67 | msgid "Norn" 68 | msgstr "" 69 | 70 | #: gw2rpc/character.py:17 71 | msgid "Sylvari" 72 | msgstr "" 73 | 74 | #: gw2rpc/character.py:20 75 | msgid "Druid" 76 | msgstr "" 77 | 78 | #: gw2rpc/character.py:21 79 | msgid "Daredevil" 80 | msgstr "" 81 | 82 | #: gw2rpc/character.py:22 83 | msgid "Berserker" 84 | msgstr "" 85 | 86 | #: gw2rpc/character.py:23 87 | msgid "Dragonhunter" 88 | msgstr "" 89 | 90 | #: gw2rpc/character.py:24 91 | msgid "Reaper" 92 | msgstr "" 93 | 94 | #: gw2rpc/character.py:25 95 | msgid "Chronomancer" 96 | msgstr "" 97 | 98 | #: gw2rpc/character.py:26 99 | msgid "Scrapper" 100 | msgstr "" 101 | 102 | #: gw2rpc/character.py:27 103 | msgid "Tempest" 104 | msgstr "" 105 | 106 | #: gw2rpc/character.py:28 107 | msgid "Herald" 108 | msgstr "" 109 | 110 | #: gw2rpc/character.py:29 111 | msgid "Soulbeast" 112 | msgstr "" 113 | 114 | #: gw2rpc/character.py:30 115 | msgid "Weaver" 116 | msgstr "" 117 | 118 | #: gw2rpc/character.py:31 119 | msgid "Holosmith" 120 | msgstr "" 121 | 122 | #: gw2rpc/character.py:32 123 | msgid "Deadeye" 124 | msgstr "" 125 | 126 | #: gw2rpc/character.py:33 127 | msgid "Mirage" 128 | msgstr "" 129 | 130 | #: gw2rpc/character.py:34 131 | msgid "Scourge" 132 | msgstr "" 133 | 134 | #: gw2rpc/character.py:35 135 | msgid "Spellbreaker" 136 | msgstr "" 137 | 138 | #: gw2rpc/character.py:36 139 | msgid "Firebrand" 140 | msgstr "" 141 | 142 | #: gw2rpc/character.py:37 143 | msgid "Renegade" 144 | msgstr "" 145 | 146 | #: gw2rpc/gw2rpc.py:39 147 | msgid "Anvil Rock" 148 | msgstr "" 149 | 150 | #: gw2rpc/gw2rpc.py:39 151 | msgid "Blackgate" 152 | msgstr "" 153 | 154 | #: gw2rpc/gw2rpc.py:39 155 | msgid "Borlis Pass" 156 | msgstr "" 157 | 158 | #: gw2rpc/gw2rpc.py:39 159 | msgid "Crystal Desert" 160 | msgstr "" 161 | 162 | #: gw2rpc/gw2rpc.py:40 163 | msgid "Darkhaven" 164 | msgstr "" 165 | 166 | #: gw2rpc/gw2rpc.py:40 167 | msgid "Devona's Rest" 168 | msgstr "" 169 | 170 | #: gw2rpc/gw2rpc.py:40 171 | msgid "Dragonbrand" 172 | msgstr "" 173 | 174 | #: gw2rpc/gw2rpc.py:40 175 | msgid "Ehmry Bay" 176 | msgstr "" 177 | 178 | #: gw2rpc/gw2rpc.py:41 179 | msgid "Eredon Terrace" 180 | msgstr "" 181 | 182 | #: gw2rpc/gw2rpc.py:41 183 | msgid "Ferguson's Crossing" 184 | msgstr "" 185 | 186 | #: gw2rpc/gw2rpc.py:41 187 | msgid "Fort Aspenwood" 188 | msgstr "" 189 | 190 | #: gw2rpc/gw2rpc.py:42 191 | msgid "Gate of Madness" 192 | msgstr "" 193 | 194 | #: gw2rpc/gw2rpc.py:42 195 | msgid "Henge of Denravi" 196 | msgstr "" 197 | 198 | #: gw2rpc/gw2rpc.py:42 199 | msgid "Isle of Janthir" 200 | msgstr "" 201 | 202 | #: gw2rpc/gw2rpc.py:43 203 | msgid "Jade Quarry" 204 | msgstr "" 205 | 206 | #: gw2rpc/gw2rpc.py:43 207 | msgid "Kaineng" 208 | msgstr "" 209 | 210 | #: gw2rpc/gw2rpc.py:43 211 | msgid "Maguuma" 212 | msgstr "" 213 | 214 | #: gw2rpc/gw2rpc.py:43 215 | msgid "Northern Shiverpeaks" 216 | msgstr "" 217 | 218 | #: gw2rpc/gw2rpc.py:44 219 | msgid "Sanctum of Rall" 220 | msgstr "" 221 | 222 | #: gw2rpc/gw2rpc.py:44 223 | msgid "Sea of Sorrows" 224 | msgstr "" 225 | 226 | #: gw2rpc/gw2rpc.py:44 227 | msgid "Sorrow's Furnace" 228 | msgstr "" 229 | 230 | #: gw2rpc/gw2rpc.py:45 231 | msgid "Stormbluff Isle" 232 | msgstr "" 233 | 234 | #: gw2rpc/gw2rpc.py:45 235 | msgid "Tarnished Coast" 236 | msgstr "" 237 | 238 | #: gw2rpc/gw2rpc.py:45 239 | msgid "Yak's Bend" 240 | msgstr "" 241 | 242 | #: gw2rpc/gw2rpc.py:48 243 | msgid "Aurora Glade" 244 | msgstr "" 245 | 246 | #: gw2rpc/gw2rpc.py:48 247 | msgid "Blacktide" 248 | msgstr "" 249 | 250 | #: gw2rpc/gw2rpc.py:48 251 | msgid "Desolation" 252 | msgstr "" 253 | 254 | #: gw2rpc/gw2rpc.py:48 255 | msgid "Far Shiverpeaks" 256 | msgstr "" 257 | 258 | #: gw2rpc/gw2rpc.py:49 259 | msgid "Fissure of Woe" 260 | msgstr "" 261 | 262 | #: gw2rpc/gw2rpc.py:49 263 | msgid "Gandara" 264 | msgstr "" 265 | 266 | #: gw2rpc/gw2rpc.py:49 267 | msgid "Gunnar's Hold" 268 | msgstr "" 269 | 270 | #: gw2rpc/gw2rpc.py:49 271 | msgid "Piken Square" 272 | msgstr "" 273 | 274 | #: gw2rpc/gw2rpc.py:50 275 | msgid "Ring of Fire" 276 | msgstr "" 277 | 278 | #: gw2rpc/gw2rpc.py:50 279 | msgid "Ruins of Surmia" 280 | msgstr "" 281 | 282 | #: gw2rpc/gw2rpc.py:50 283 | msgid "Seafarer's Rest" 284 | msgstr "" 285 | 286 | #: gw2rpc/gw2rpc.py:50 287 | msgid "Underworld" 288 | msgstr "" 289 | 290 | #: gw2rpc/gw2rpc.py:51 291 | msgid "Arborstone [FR]" 292 | msgstr "" 293 | 294 | #: gw2rpc/gw2rpc.py:51 295 | msgid "Augury Rock [FR]" 296 | msgstr "" 297 | 298 | #: gw2rpc/gw2rpc.py:51 299 | msgid "Vabbi" 300 | msgstr "" 301 | 302 | #: gw2rpc/gw2rpc.py:51 303 | msgid "Whiteside Ridge" 304 | msgstr "" 305 | 306 | #: gw2rpc/gw2rpc.py:52 307 | msgid "Fort Ranik [FR]" 308 | msgstr "" 309 | 310 | #: gw2rpc/gw2rpc.py:52 311 | msgid "Jade Sea [FR]" 312 | msgstr "" 313 | 314 | #: gw2rpc/gw2rpc.py:52 315 | msgid "Vizunah Square [FR]" 316 | msgstr "" 317 | 318 | #: gw2rpc/gw2rpc.py:53 319 | msgid "Abaddon's Mouth [DE]" 320 | msgstr "" 321 | 322 | #: gw2rpc/gw2rpc.py:53 323 | msgid "Drakkar Lake [DE]" 324 | msgstr "" 325 | 326 | #: gw2rpc/gw2rpc.py:53 327 | msgid "Dzagonur [DE]" 328 | msgstr "" 329 | 330 | #: gw2rpc/gw2rpc.py:54 331 | msgid "Elona Reach [DE]" 332 | msgstr "" 333 | 334 | #: gw2rpc/gw2rpc.py:54 335 | msgid "Kodash [DE]" 336 | msgstr "" 337 | 338 | #: gw2rpc/gw2rpc.py:54 339 | msgid "Miller's Sound [DE]" 340 | msgstr "" 341 | 342 | #: gw2rpc/gw2rpc.py:55 343 | msgid "Baruch Bay [SP]" 344 | msgstr "" 345 | 346 | #: gw2rpc/gw2rpc.py:55 347 | msgid "Riverside [DE]" 348 | msgstr "" 349 | 350 | #: gw2rpc/gw2rpc.py:92 351 | msgid "About" 352 | msgstr "" 353 | 354 | #: gw2rpc/gw2rpc.py:94 355 | msgid "Join support server" 356 | msgstr "" 357 | 358 | #: gw2rpc/gw2rpc.py:97 359 | msgid "Guild Wars 2 with Discord" 360 | msgstr "" 361 | 362 | #: gw2rpc/gw2rpc.py:137 363 | msgid "Could not check for updates - check your connection!" 364 | msgstr "" 365 | 366 | #: gw2rpc/gw2rpc.py:143 367 | msgid "There is a new update for GW2 Rich Presence available. Would you like to be taken to the download page now?" 368 | msgstr "" 369 | 370 | #: gw2rpc/gw2rpc.py:165 371 | msgid "Fractals of the Mists" 372 | msgstr "" 373 | 374 | #: gw2rpc/gw2rpc.py:170 375 | msgid " fractal" 376 | msgstr "" 377 | 378 | #: gw2rpc/gw2rpc.py:198 gw2rpc/gw2rpc.py:199 379 | msgid "in " 380 | msgstr "" 381 | 382 | #: gw2rpc/gw2rpc.py:214 383 | msgid "fighting " 384 | msgstr "" 385 | 386 | #: gw2rpc/gw2rpc.py:216 387 | msgid "completing " 388 | msgstr "" 389 | 390 | #: gw2rpc/gw2rpc.py:285 391 | msgid " near " 392 | msgstr "" 393 | 394 | #: gw2rpc/gw2rpc.py:302 395 | msgid "in character selection" 396 | msgstr "" 397 | 398 | #: gw2rpc/gw2rpc.py:307 399 | msgid "Character Selection" 400 | msgstr "" 401 | 402 | 403 | msgid "Mistlock Observatory" 404 | msgstr "" 405 | 406 | msgid "Uncategorized" 407 | msgstr "" 408 | 409 | msgid "Snowblind" 410 | msgstr "" 411 | 412 | msgid "Swampland" 413 | msgstr "" 414 | 415 | msgid "Urban Battleground" 416 | msgstr "" 417 | 418 | msgid "Siren's Reef" 419 | msgstr "" 420 | 421 | msgid "Aquatic Ruins" 422 | msgstr "" 423 | 424 | msgid "Cliffside" 425 | msgstr "" 426 | 427 | msgid "Underground Facility" 428 | msgstr "" 429 | 430 | msgid "Volcanic" 431 | msgstr "" 432 | 433 | msgid "Molten Furnace" 434 | msgstr "" 435 | 436 | msgid "Aetherblade" 437 | msgstr "" 438 | 439 | msgid "Thaumanova Reactor" 440 | msgstr "" 441 | 442 | msgid "Solid Ocean" 443 | msgstr "" 444 | 445 | msgid "Molten Boss" 446 | msgstr "" 447 | 448 | msgid "Mai Trin" 449 | msgstr "" 450 | 451 | msgid "Chaos" 452 | msgstr "" 453 | 454 | msgid "Nightmare" 455 | msgstr "" 456 | 457 | msgid "Shattered Observatory" 458 | msgstr "" 459 | 460 | msgid "Twilight Oasis" 461 | msgstr "" 462 | 463 | msgid "Deepstone" 464 | msgstr "" 465 | 466 | msgid "Sunqua Peak" 467 | msgstr "" 468 | 469 | msgid "Skorvald" 470 | msgstr "" 471 | 472 | msgid "Ensolyss" 473 | msgstr "" 474 | 475 | msgid "Ai" 476 | msgstr "" 477 | 478 | msgid "Artsariiv" 479 | msgstr "" 480 | 481 | msgid "Arkk" 482 | msgstr "" 483 | 484 | msgid "M.A.M.A." 485 | msgstr "" 486 | 487 | msgid "Siax" 488 | msgstr "" 489 | 490 | msgid "Vale Guardian" 491 | msgstr "" 492 | 493 | msgid "Spirit Woods" 494 | msgstr "" 495 | 496 | msgid "Gorseval" 497 | msgstr "" 498 | 499 | msgid "Sabetha" 500 | msgstr "" 501 | 502 | msgid "Slothasor" 503 | msgstr "" 504 | 505 | msgid "Bandit Trio" 506 | msgstr "" 507 | 508 | msgid "Matthias" 509 | msgstr "" 510 | 511 | msgid "Escort" 512 | msgstr "" 513 | 514 | msgid "Keep Construct" 515 | msgstr "" 516 | 517 | msgid "Xera" 518 | msgstr "" 519 | 520 | msgid "Twisted Castle" 521 | msgstr "" 522 | 523 | msgid "Cairn" 524 | msgstr "" 525 | 526 | msgid "Mursaat Overseer" 527 | msgstr "" 528 | 529 | msgid "Samarog" 530 | msgstr "" 531 | 532 | msgid "Deimos" 533 | msgstr "" 534 | 535 | msgid "Soulless Horror" 536 | msgstr "" 537 | 538 | msgid "River of Souls" 539 | msgstr "" 540 | 541 | msgid "Broken King" 542 | msgstr "" 543 | 544 | msgid "Eater of Souls" 545 | msgstr "" 546 | 547 | msgid "Dhuum" 548 | msgstr "" 549 | 550 | msgid "Statue of Darkness" 551 | msgstr "" 552 | 553 | msgid "Conjured Amalgamate" 554 | msgstr "" 555 | 556 | msgid "Twin Largos" 557 | msgstr "" 558 | 559 | msgid "Qadim" 560 | msgstr "" 561 | 562 | msgid "Cardinal Sabir" 563 | msgstr "" 564 | 565 | msgid "Cardinal Adina" 566 | msgstr "" 567 | 568 | msgid "Qadim the Peerless" 569 | msgstr "" 570 | 571 | msgid "Harbinger" 572 | msgstr "" 573 | 574 | msgid "Willbender" 575 | msgstr "" 576 | 577 | msgid "Virtuoso" 578 | msgstr "" 579 | 580 | msgid "Catalyst" 581 | msgstr "" 582 | 583 | msgid "Bladesworn" 584 | msgstr "" 585 | 586 | msgid "Vindicator" 587 | msgstr "" 588 | 589 | msgid "Mechanist" 590 | msgstr "" 591 | 592 | msgid "Specter" 593 | msgstr "" 594 | 595 | msgid "Untamed" 596 | msgstr "" 597 | 598 | msgid "Commander" 599 | msgstr "" 600 | 601 | msgid "jackal" 602 | msgstr "" 603 | 604 | msgid "griffon" 605 | msgstr "" 606 | 607 | msgid "springer" 608 | msgstr "" 609 | 610 | msgid "skimmer" 611 | msgstr "" 612 | 613 | msgid "raptor" 614 | msgstr "" 615 | 616 | msgid "roller beetle" 617 | msgstr "" 618 | 619 | msgid "warclaw" 620 | msgstr "" 621 | 622 | msgid "skyscale" 623 | msgstr "" 624 | 625 | msgid "siege turtle" 626 | msgstr "" 627 | 628 | msgid "on" 629 | msgstr "" 630 | 631 | msgid "Whisper of Jormag" 632 | msgstr "" 633 | 634 | msgid "Boneskinner" 635 | msgstr "" 636 | 637 | msgid "Fraenir of Jormag" 638 | msgstr "" 639 | 640 | msgid "Voice And Claw of the Fallen" 641 | msgstr "" 642 | 643 | msgid "Legendary Icebrood Construct" 644 | msgstr "" 645 | 646 | msgid "Mistlock Sanctuary" 647 | msgstr "" 648 | 649 | msgid "Closest" 650 | msgstr "" 651 | 652 | # Molten boss fractal 653 | msgid "Berserker and Firestorm" 654 | msgstr "" 655 | 656 | # Sirens reef fractal 657 | msgid "Blasting Black Peter" 658 | msgstr "" 659 | 660 | # Sirens reef fractal 661 | msgid "Arabella Crowe" 662 | msgstr "" 663 | 664 | msgid "Brood Queen" 665 | msgstr "" 666 | 667 | msgid "Deepstone Sentinel" 668 | msgstr "" 669 | 670 | msgid "The Voice" 671 | msgstr "" 672 | 673 | msgid "Bloomhunger" 674 | msgstr "" 675 | 676 | msgid "Jade Maw" 677 | msgstr "" 678 | 679 | msgid "loading screen" 680 | msgstr "" 681 | 682 | msgid "GW2RPC Raid Announcer" 683 | msgstr "" 684 | 685 | msgid "tagged up" 686 | msgstr "" 687 | 688 | msgid "Copy and paste the following to join" 689 | msgstr "" 690 | 691 | msgid "Yes" 692 | msgstr "" 693 | 694 | msgid "No" 695 | msgstr "" 696 | 697 | msgid "Announce raids:" 698 | msgstr "" 699 | 700 | msgid "Closest PoI" 701 | msgstr "" 702 | 703 | msgid "skiff" 704 | msgstr "" 705 | 706 | msgid "Jade Bot" 707 | msgstr "" 708 | 709 | msgid "Elemental Source" 710 | msgstr "" 711 | 712 | msgid "Lornar Dragonseeker" 713 | msgstr "" 714 | 715 | msgid "Frizz" 716 | msgstr "" 717 | 718 | msgid "Captain Mai Trin" 719 | msgstr "" 720 | 721 | msgid "Anomaly" 722 | msgstr "" 723 | 724 | msgid "Brazen Gladiator" 725 | msgstr "" 726 | 727 | msgid "Amala" 728 | msgstr "" 729 | 730 | msgid "a sandbinder" 731 | msgstr "" 732 | 733 | msgid "Captain Ashym" 734 | msgstr "" 735 | 736 | msgid "Jellyfish Beast" 737 | msgstr "" 738 | 739 | msgid "Archdiviner" 740 | msgstr "" 741 | 742 | msgid "Rabsovich" 743 | msgstr "" 744 | 745 | msgid "Rampaging Ice Elemental or Dredge Powersuit" 746 | msgstr "" 747 | 748 | msgid "Grawl Shaman" 749 | msgstr "" 750 | 751 | msgid "Imbued Shaman" 752 | msgstr "" 753 | 754 | msgid "in Weapon Testing Facility" 755 | msgstr "" 756 | 757 | msgid "Subject 6" 758 | msgstr "" 759 | 760 | msgid "Thaumanova Anomaly" 761 | msgstr "" 762 | 763 | # pre-event of Wing 7 764 | msgid "gate" 765 | msgstr "" 766 | 767 | msgid "Minister Li" 768 | msgstr "" 769 | 770 | msgid "Old Tom" 771 | msgstr "" 772 | 773 | msgid "Raving Asura" 774 | msgstr "" 775 | 776 | msgid "Voice of the Mountain" 777 | msgstr "" 778 | 779 | msgid "Sorrow of the Mountain" 780 | msgstr "" 781 | 782 | msgid "Fury of the Mountain" 783 | msgstr "" 784 | 785 | msgid "Dragon Void" 786 | msgstr "" -------------------------------------------------------------------------------- /locales/de/LC_MESSAGES/base.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR ORGANIZATION 3 | # FIRST AUTHOR fabi@ihlecloud.de, 2021. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: 2.1\n" 8 | "POT-Creation-Date: 2021-05-28 15:15+0200\n" 9 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 10 | "Last-Translator: FULL NAME \n" 11 | "Language-Team: LANGUAGE \n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Generated-By: pygettext.py 1.5\n" 16 | 17 | 18 | #: gw2rpc/character.py:6 19 | msgid "Guardian" 20 | msgstr "Wächter" 21 | 22 | #: gw2rpc/character.py:7 23 | msgid "Warrior" 24 | msgstr "Krieger" 25 | 26 | #: gw2rpc/character.py:8 27 | msgid "Engineer" 28 | msgstr "Ingenieur" 29 | 30 | #: gw2rpc/character.py:9 31 | msgid "Ranger" 32 | msgstr "Waldläufer" 33 | 34 | #: gw2rpc/character.py:10 35 | msgid "Thief" 36 | msgstr "Dieb" 37 | 38 | #: gw2rpc/character.py:11 39 | msgid "Elementalist" 40 | msgstr "Elementarmagier" 41 | 42 | #: gw2rpc/character.py:12 43 | msgid "Mesmer" 44 | msgstr "Mesmer" 45 | 46 | #: gw2rpc/character.py:13 47 | msgid "Necromancer" 48 | msgstr "Nekromant" 49 | 50 | #: gw2rpc/character.py:14 51 | msgid "Revenant" 52 | msgstr "Widergänger" 53 | 54 | #: gw2rpc/character.py:17 55 | msgid "Asura" 56 | msgstr "Asura" 57 | 58 | #: gw2rpc/character.py:17 59 | msgid "Charr" 60 | msgstr "Charr" 61 | 62 | #: gw2rpc/character.py:17 63 | msgid "Human" 64 | msgstr "Mensch" 65 | 66 | #: gw2rpc/character.py:17 67 | msgid "Norn" 68 | msgstr "Norn" 69 | 70 | #: gw2rpc/character.py:17 71 | msgid "Sylvari" 72 | msgstr "Sylvari" 73 | 74 | #: gw2rpc/character.py:20 75 | msgid "Druid" 76 | msgstr "Druide" 77 | 78 | #: gw2rpc/character.py:21 79 | msgid "Daredevil" 80 | msgstr "Draufgänger" 81 | 82 | #: gw2rpc/character.py:22 83 | msgid "Berserker" 84 | msgstr "Berserker" 85 | 86 | #: gw2rpc/character.py:23 87 | msgid "Dragonhunter" 88 | msgstr "Drachenjäger" 89 | 90 | #: gw2rpc/character.py:24 91 | msgid "Reaper" 92 | msgstr "Schnitter" 93 | 94 | #: gw2rpc/character.py:25 95 | msgid "Chronomancer" 96 | msgstr "Chronomant" 97 | 98 | #: gw2rpc/character.py:26 99 | msgid "Scrapper" 100 | msgstr "Schrotter" 101 | 102 | #: gw2rpc/character.py:27 103 | msgid "Tempest" 104 | msgstr "Sturmbote" 105 | 106 | #: gw2rpc/character.py:28 107 | msgid "Herald" 108 | msgstr "Herold" 109 | 110 | #: gw2rpc/character.py:29 111 | msgid "Soulbeast" 112 | msgstr "Seelenwandler" 113 | 114 | #: gw2rpc/character.py:30 115 | msgid "Weaver" 116 | msgstr "Weber" 117 | 118 | #: gw2rpc/character.py:31 119 | msgid "Holosmith" 120 | msgstr "Holoschmied" 121 | 122 | #: gw2rpc/character.py:32 123 | msgid "Deadeye" 124 | msgstr "Scharfschütze" 125 | 126 | #: gw2rpc/character.py:33 127 | msgid "Mirage" 128 | msgstr "Illusionist" 129 | 130 | #: gw2rpc/character.py:34 131 | msgid "Scourge" 132 | msgstr "Pestbringer" 133 | 134 | #: gw2rpc/character.py:35 135 | msgid "Spellbreaker" 136 | msgstr "Bannbrecher" 137 | 138 | #: gw2rpc/character.py:36 139 | msgid "Firebrand" 140 | msgstr "Aufwiegler" 141 | 142 | #: gw2rpc/character.py:37 143 | msgid "Renegade" 144 | msgstr "Abtrünniger" 145 | 146 | #: gw2rpc/gw2rpc.py:39 147 | msgid "Anvil Rock" 148 | msgstr "Amboss-Stein" 149 | 150 | #: gw2rpc/gw2rpc.py:39 151 | msgid "Blackgate" 152 | msgstr "Schwarztor" 153 | 154 | #: gw2rpc/gw2rpc.py:39 155 | msgid "Borlis Pass" 156 | msgstr "Borlis-Pass" 157 | 158 | #: gw2rpc/gw2rpc.py:39 159 | msgid "Crystal Desert" 160 | msgstr "Kristallwüste" 161 | 162 | #: gw2rpc/gw2rpc.py:40 163 | msgid "Darkhaven" 164 | msgstr "Finsterfreistatt" 165 | 166 | #: gw2rpc/gw2rpc.py:40 167 | msgid "Devona's Rest" 168 | msgstr "Devonas Ruh" 169 | 170 | #: gw2rpc/gw2rpc.py:40 171 | msgid "Dragonbrand" 172 | msgstr "Drachenbrand" 173 | 174 | #: gw2rpc/gw2rpc.py:40 175 | msgid "Ehmry Bay" 176 | msgstr "Ehmry-Bucht" 177 | 178 | #: gw2rpc/gw2rpc.py:41 179 | msgid "Eredon Terrace" 180 | msgstr "Eredon-Terrasse" 181 | 182 | #: gw2rpc/gw2rpc.py:41 183 | msgid "Ferguson's Crossing" 184 | msgstr "Fergusons-Kreuzung" 185 | 186 | #: gw2rpc/gw2rpc.py:41 187 | msgid "Fort Aspenwood" 188 | msgstr "Fort Epsenwald" 189 | 190 | #: gw2rpc/gw2rpc.py:42 191 | msgid "Gate of Madness" 192 | msgstr "Tor des Wahnsinns" 193 | 194 | #: gw2rpc/gw2rpc.py:42 195 | msgid "Henge of Denravi" 196 | msgstr "Steinkreis von Denravi" 197 | 198 | #: gw2rpc/gw2rpc.py:42 199 | msgid "Isle of Janthir" 200 | msgstr "Janthir-Insel" 201 | 202 | #: gw2rpc/gw2rpc.py:43 203 | msgid "Jade Quarry" 204 | msgstr "Jade-Steinbruch" 205 | 206 | #: gw2rpc/gw2rpc.py:43 207 | msgid "Kaineng" 208 | msgstr "Kaineng" 209 | 210 | #: gw2rpc/gw2rpc.py:43 211 | msgid "Maguuma" 212 | msgstr "Maguuma" 213 | 214 | #: gw2rpc/gw2rpc.py:43 215 | msgid "Northern Shiverpeaks" 216 | msgstr "Nördliche Zittergipfel" 217 | 218 | #: gw2rpc/gw2rpc.py:44 219 | msgid "Sanctum of Rall" 220 | msgstr "Heilige Halle von Rall" 221 | 222 | #: gw2rpc/gw2rpc.py:44 223 | msgid "Sea of Sorrows" 224 | msgstr "Meer des Leids" 225 | 226 | #: gw2rpc/gw2rpc.py:44 227 | msgid "Sorrow's Furnace" 228 | msgstr "Hochofen der Betrübnis" 229 | 230 | #: gw2rpc/gw2rpc.py:45 231 | msgid "Stormbluff Isle" 232 | msgstr "Sturmklippen-Insel" 233 | 234 | #: gw2rpc/gw2rpc.py:45 235 | msgid "Tarnished Coast" 236 | msgstr "Befleckte Küste" 237 | 238 | #: gw2rpc/gw2rpc.py:45 239 | msgid "Yak's Bend" 240 | msgstr "Yakbiegung" 241 | 242 | #: gw2rpc/gw2rpc.py:48 243 | msgid "Aurora Glade" 244 | msgstr "Auroralichtung" 245 | 246 | #: gw2rpc/gw2rpc.py:48 247 | msgid "Blacktide" 248 | msgstr "Schwarzwasser" 249 | 250 | #: gw2rpc/gw2rpc.py:48 251 | msgid "Desolation" 252 | msgstr "Ödnis" 253 | 254 | #: gw2rpc/gw2rpc.py:48 255 | msgid "Far Shiverpeaks" 256 | msgstr "Ferne Zittergipfel" 257 | 258 | #: gw2rpc/gw2rpc.py:49 259 | msgid "Fissure of Woe" 260 | msgstr "Riss des Kummers" 261 | 262 | #: gw2rpc/gw2rpc.py:49 263 | msgid "Gandara" 264 | msgstr "Gandara" 265 | 266 | #: gw2rpc/gw2rpc.py:49 267 | msgid "Gunnar's Hold" 268 | msgstr "Gunnars Feste" 269 | 270 | #: gw2rpc/gw2rpc.py:49 271 | msgid "Piken Square" 272 | msgstr "Piken-Platz" 273 | 274 | #: gw2rpc/gw2rpc.py:50 275 | msgid "Ring of Fire" 276 | msgstr "Feuerring" 277 | 278 | #: gw2rpc/gw2rpc.py:50 279 | msgid "Ruins of Surmia" 280 | msgstr "Ruinen von Surmia" 281 | 282 | #: gw2rpc/gw2rpc.py:50 283 | msgid "Seafarer's Rest" 284 | msgstr "Seemannsruh" 285 | 286 | #: gw2rpc/gw2rpc.py:50 287 | msgid "Underworld" 288 | msgstr "Unterwelt" 289 | 290 | #: gw2rpc/gw2rpc.py:51 291 | msgid "Arborstone [FR]" 292 | msgstr "Arborstein [FR]" 293 | 294 | #: gw2rpc/gw2rpc.py:51 295 | msgid "Augury Rock [FR]" 296 | msgstr "Fels der Weissagung [FR]" 297 | 298 | #: gw2rpc/gw2rpc.py:51 299 | msgid "Vabbi" 300 | msgstr "Vaabi" 301 | 302 | #: gw2rpc/gw2rpc.py:51 303 | msgid "Whiteside Ridge" 304 | msgstr "Weißflankgrat" 305 | 306 | #: gw2rpc/gw2rpc.py:52 307 | msgid "Fort Ranik [FR]" 308 | msgstr "Fort Ranik [FR]" 309 | 310 | #: gw2rpc/gw2rpc.py:52 311 | msgid "Jade Sea [FR]" 312 | msgstr "Jademeer [FR]" 313 | 314 | #: gw2rpc/gw2rpc.py:52 315 | msgid "Vizunah Square [FR]" 316 | msgstr "Vizunah-Platz [FR]" 317 | 318 | #: gw2rpc/gw2rpc.py:53 319 | msgid "Abaddon's Mouth [DE]" 320 | msgstr "Abaddons Maul [DE]" 321 | 322 | #: gw2rpc/gw2rpc.py:53 323 | msgid "Drakkar Lake [DE]" 324 | msgstr "Drakkar-See [DE]" 325 | 326 | #: gw2rpc/gw2rpc.py:53 327 | msgid "Dzagonur [DE]" 328 | msgstr "Dzagonur [DE]" 329 | 330 | #: gw2rpc/gw2rpc.py:54 331 | msgid "Elona Reach [DE]" 332 | msgstr "Elonaspitze [DE]" 333 | 334 | #: gw2rpc/gw2rpc.py:54 335 | msgid "Kodash [DE]" 336 | msgstr "Kodash [DE]" 337 | 338 | #: gw2rpc/gw2rpc.py:54 339 | msgid "Miller's Sound [DE]" 340 | msgstr "Millersund [DE]" 341 | 342 | #: gw2rpc/gw2rpc.py:55 343 | msgid "Baruch Bay [SP]" 344 | msgstr "Baruch Bucht [ES]" 345 | 346 | #: gw2rpc/gw2rpc.py:55 347 | msgid "Riverside [DE]" 348 | msgstr "Flussufer [DE]" 349 | 350 | #: gw2rpc/gw2rpc.py:92 351 | msgid "About" 352 | msgstr "Über..." 353 | 354 | #: gw2rpc/gw2rpc.py:94 355 | msgid "Join support server" 356 | msgstr "Support Server beitreten" 357 | 358 | #: gw2rpc/gw2rpc.py:97 359 | msgid "Guild Wars 2 with Discord" 360 | msgstr "Guild Wars 2 mit Discord" 361 | 362 | #: gw2rpc/gw2rpc.py:137 363 | msgid "Could not check for updates - check your connection!" 364 | msgstr "Konnte nicht nach Updates suchen - Bitte Verbindung prüfen!" 365 | 366 | #: gw2rpc/gw2rpc.py:143 367 | msgid "There is a new update for GW2 Rich Presence available. Would you like to be taken to the download page now?" 368 | msgstr "Es ist eine neue Version von GW2 Rich Presence verfügbar. Möchtest du jetzt zur Download-Seite wechseln?" 369 | 370 | #: gw2rpc/gw2rpc.py:165 371 | msgid "Fractals of the Mists" 372 | msgstr "Fraktale der Nebel" 373 | 374 | #: gw2rpc/gw2rpc.py:170 375 | msgid "fractal" 376 | msgstr "Fraktal" 377 | 378 | #: gw2rpc/gw2rpc.py:198 gw2rpc/gw2rpc.py:199 379 | msgid "in " 380 | msgstr "in " 381 | 382 | #: gw2rpc/gw2rpc.py:214 383 | msgid "fighting " 384 | msgstr "bekämpft " 385 | 386 | #: gw2rpc/gw2rpc.py:216 387 | msgid "completing " 388 | msgstr "beendet " 389 | 390 | #: gw2rpc/gw2rpc.py:285 391 | msgid " near " 392 | msgstr " bei " 393 | 394 | #: gw2rpc/gw2rpc.py:302 395 | msgid "in character selection" 396 | msgstr "in der Charakterauswahl" 397 | 398 | #: gw2rpc/gw2rpc.py:307 399 | msgid "Character Selection" 400 | msgstr "Charakterauswahl" 401 | 402 | msgid "Mistlock Observatory" 403 | msgstr "Nebelsperren Observatorium" 404 | 405 | msgid "Uncategorized" 406 | msgstr "Nicht kategorisiert" 407 | 408 | msgid "Snowblind" 409 | msgstr "Schneeblind" 410 | 411 | msgid "Swampland" 412 | msgstr "Sumpfland" 413 | 414 | msgid "Urban Battleground" 415 | msgstr "Urbanes Schlachtfeld" 416 | 417 | msgid "Aquatic Ruins" 418 | msgstr "Unterwasserruinen" 419 | 420 | msgid "Siren's Reef" 421 | msgstr "Riff der Sirene" 422 | 423 | msgid "Cliffside" 424 | msgstr "Felswand" 425 | 426 | msgid "Underground Facility" 427 | msgstr "Untergrundeinrichtung" 428 | 429 | msgid "Volcanic" 430 | msgstr "Vulkanisch" 431 | 432 | msgid "Molten Furnace" 433 | msgstr "Feuriger Hochofen" 434 | 435 | msgid "Aetherblade" 436 | msgstr "Ätherklinge" 437 | 438 | msgid "Thaumanova Reactor" 439 | msgstr "Thaumanova-Reaktor" 440 | 441 | msgid "Solid Ocean" 442 | msgstr "Solider Ozean" 443 | 444 | msgid "Molten Boss" 445 | msgstr "Feuriger Boss" 446 | 447 | msgid "Mai Trin" 448 | msgstr "Mai Trin Boss" 449 | 450 | msgid "Chaos" 451 | msgstr "Chaos" 452 | 453 | msgid "Nightmare" 454 | msgstr "Albtraum" 455 | 456 | msgid "Shattered Observatory" 457 | msgstr "Zerschmettertes Observatorium" 458 | 459 | msgid "Twilight Oasis" 460 | msgstr "Zwielichtoase" 461 | 462 | msgid "Deepstone" 463 | msgstr "Tiefenstein" 464 | 465 | msgid "Sunqua Peak" 466 | msgstr "Sunqua-Gipfel" 467 | 468 | msgid "Skorvald" 469 | msgstr "Skorvald" 470 | 471 | msgid "Ensolyss" 472 | msgstr "Ensolyss" 473 | 474 | msgid "Ai" 475 | msgstr "Ai" 476 | 477 | msgid "Artsariiv" 478 | msgstr "Artsariiv" 479 | 480 | msgid "Arkk" 481 | msgstr "Arkk" 482 | 483 | msgid "M.A.M.A." 484 | msgstr "M.A.M.A." 485 | 486 | msgid "Siax" 487 | msgstr "Siax" 488 | 489 | msgid "Vale Guardian" 490 | msgstr "Talwächter" 491 | 492 | msgid "Spirit Woods" 493 | msgstr "Geisterlauf" 494 | 495 | msgid "Gorseval" 496 | msgstr "" 497 | 498 | msgid "Sabetha" 499 | msgstr "" 500 | 501 | msgid "Slothasor" 502 | msgstr "Faultierion" 503 | 504 | msgid "Bandit Trio" 505 | msgstr "Banditen-Trio" 506 | 507 | msgid "Matthias" 508 | msgstr "" 509 | 510 | msgid "Escort" 511 | msgstr "Belagert die Festung" 512 | 513 | msgid "Keep Construct" 514 | msgstr "Festenkonstrukt" 515 | 516 | msgid "Xera" 517 | msgstr "" 518 | 519 | msgid "Twisted Castle" 520 | msgstr "Verdrehtes Schloss" 521 | 522 | msgid "Cairn" 523 | msgstr "" 524 | 525 | msgid "Mursaat Overseer" 526 | msgstr "Mursaat-Aufseher" 527 | 528 | msgid "Samarog" 529 | msgstr "" 530 | 531 | msgid "Deimos" 532 | msgstr "" 533 | 534 | msgid "Soulless Horror" 535 | msgstr "Seelenloser Schrecken" 536 | 537 | msgid "River of Souls" 538 | msgstr "Fluss der Seelen" 539 | 540 | msgid "Broken King" 541 | msgstr "Gebrochener König" 542 | 543 | msgid "Eater of Souls" 544 | msgstr "Seelenverzehrer" 545 | 546 | msgid "Dhuum" 547 | msgstr "" 548 | 549 | msgid "Statue of Darkness" 550 | msgstr "Statue der Dunkelheit" 551 | 552 | msgid "Conjured Amalgamate" 553 | msgstr "Beschworene Verschmelzung" 554 | 555 | msgid "Twin Largos" 556 | msgstr "Zwillings-Largos" 557 | 558 | msgid "Qadim" 559 | msgstr "" 560 | 561 | msgid "Cardinal Sabir" 562 | msgstr "Kardinal Sabir" 563 | 564 | msgid "Cardinal Adina" 565 | msgstr "Kardinal Adina" 566 | 567 | msgid "Qadim the Peerless" 568 | msgstr "Qadim der Unvergleichliche" 569 | 570 | msgid "Harbinger" 571 | msgstr "Vorbote" 572 | 573 | msgid "Willbender" 574 | msgstr "Willensverdreher" 575 | 576 | msgid "Virtuoso" 577 | msgstr "Virtuose" 578 | 579 | msgid "Catalyst" 580 | msgstr "Katalysierer" 581 | 582 | msgid "Bladesworn" 583 | msgstr "Klingengeschworene" 584 | 585 | msgid "Vindicator" 586 | msgstr "Rechtssuchende" 587 | 588 | msgid "Mechanist" 589 | msgstr "Mech-Lenker" 590 | 591 | msgid "Specter" 592 | msgstr "Phantom" 593 | 594 | msgid "Untamed" 595 | msgstr "Ungezähmter" 596 | 597 | msgid "Commander" 598 | msgstr "Kommandeur" 599 | 600 | msgid "jackal" 601 | msgstr "Schakal" 602 | 603 | msgid "griffon" 604 | msgstr "Greif" 605 | 606 | msgid "springer" 607 | msgstr "Springer" 608 | 609 | msgid "skimmer" 610 | msgstr "Schweberochen" 611 | 612 | msgid "raptor" 613 | msgstr "Raptor" 614 | 615 | msgid "roller beetle" 616 | msgstr "Rollkäfer" 617 | 618 | msgid "warclaw" 619 | msgstr "Kriegsklaue" 620 | 621 | msgid "skyscale" 622 | msgstr "Himmelsschuppe" 623 | 624 | msgid "siege turtle" 625 | msgstr "Belagerungs-Schildkröte" 626 | 627 | msgid "on" 628 | msgstr "auf" 629 | 630 | msgid "Whisper of Jormag" 631 | msgstr "Geflüster des Jormag" 632 | 633 | msgid "Boneskinner" 634 | msgstr "Knochenhäuter" 635 | 636 | msgid "Fraenir of Jormag" 637 | msgstr "Fraenir Jormags" 638 | 639 | msgid "Voice And Claw of the Fallen" 640 | msgstr "Stimme und Klaue der Gefallenen" 641 | 642 | msgid "Legendary Icebrood Construct" 643 | msgstr "Legende Eisbrut-Konstrukt" 644 | 645 | msgid "Mistlock Sanctuary" 646 | msgstr "Nebelsperren-Zuflucht" 647 | 648 | msgid "Closest" 649 | msgstr "Nächster" 650 | 651 | # Molten boss fractal 652 | msgid "Berserker and Firestorm" 653 | msgstr "Berserker und Feuersturm" 654 | 655 | # Sirens reef fractal 656 | msgid "Blasting Black Peter" 657 | msgstr "Schwarzpulver-Peter" 658 | 659 | # Sirens reef fractal 660 | msgid "Arabella Crowe" 661 | msgstr "Arabella Krähe" 662 | 663 | msgid "Brood Queen" 664 | msgstr "Brutkönigin" 665 | 666 | msgid "Deepstone Sentinel" 667 | msgstr "Tiefenstein-Wache" 668 | 669 | msgid "The Voice" 670 | msgstr "Die Stimme" 671 | 672 | msgid "Bloomhunger" 673 | msgstr "Blütenschmacht" 674 | 675 | msgid "Jade Maw" 676 | msgstr "Jadeschlund" 677 | 678 | msgid "loading screen" 679 | msgstr "Ladebildschirm" 680 | 681 | msgid "GW2RPC Raid Announcer" 682 | msgstr "GW2RPC Raid Ankündigung" 683 | 684 | msgid "tagged up" 685 | msgstr "hat einen Raid gestartet" 686 | 687 | msgid "Copy and paste the following to join" 688 | msgstr "Kopiere folgendes und füge es ein um beizutreten" 689 | 690 | msgid "Yes" 691 | msgstr "Ja" 692 | 693 | msgid "No" 694 | msgstr "Nein" 695 | 696 | msgid "Announce raids:" 697 | msgstr "Raid ankündigen:" 698 | 699 | msgid "Closest PoI" 700 | msgstr "Nächster PoI" 701 | 702 | msgid "skiff" 703 | msgstr "Skiff" 704 | 705 | msgid "Jade Bot" 706 | msgstr "Jade-Bot" 707 | 708 | msgid "Elemental Source" 709 | msgstr "elementare Quelle" 710 | 711 | msgid "Lornar Dragonseeker" 712 | msgstr "Lornarr Dragonseeker" 713 | 714 | msgid "Frizz" 715 | msgstr "Frizz" 716 | 717 | msgid "Captain Mai Trin" 718 | msgstr "Kaptain Mai Trin" 719 | 720 | msgid "Anomaly" 721 | msgstr "Anomalie" 722 | 723 | msgid "Brazen Gladiator" 724 | msgstr "Dreister Gladiator" 725 | 726 | msgid "Amala" 727 | msgstr "Amala" 728 | 729 | msgid "a sandbinder" 730 | msgstr "ein Sandbanner" 731 | 732 | msgid "Captain Ashym" 733 | msgstr "Hauptmann Ashym" 734 | 735 | msgid "Jellyfish Beast" 736 | msgstr "Quallenbestie" 737 | 738 | msgid "Archdiviner" 739 | msgstr "Erzwahrsager" 740 | 741 | msgid "Rabsovich" 742 | msgstr "" 743 | 744 | msgid "Rampaging Ice Elemental or Dredge Powersuit" 745 | msgstr "Schaufler-Minenanzug oder Eiselementar" 746 | 747 | msgid "Grawl Shaman" 748 | msgstr "Grawl-Schamane" 749 | 750 | msgid "Imbued Shaman" 751 | msgstr "durchdrungener Schamanen" 752 | 753 | msgid "in Weapon Testing Facility" 754 | msgstr "" 755 | 756 | msgid "Subject 6" 757 | msgstr "Subjekt 6" 758 | 759 | msgid "Thaumanova Anomaly" 760 | msgstr "Thaumanova-Anomalie" 761 | 762 | # pre-event of Wing 7 763 | msgid "gate" 764 | msgstr "" 765 | 766 | msgid "Minister Li" 767 | msgstr "" 768 | 769 | msgid "Old Tom" 770 | msgstr "Alter Tom" 771 | 772 | msgid "Raving Asura" 773 | msgstr "Wütender Asura" 774 | 775 | msgid "Voice of the Mountain" 776 | msgstr "Stimme des Bergs" 777 | 778 | msgid "Sorrow of the Mountain" 779 | msgstr "Kummer des Bergs" 780 | 781 | msgid "Fury of the Mountain" 782 | msgstr "Zorn des Bergs" 783 | 784 | msgid "Dragon Void" 785 | msgstr "die Drachenleere" 786 | 787 | msgid "Silent Surf" 788 | msgstr "Stumme Brandung" -------------------------------------------------------------------------------- /locales/en/LC_MESSAGES/base.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR ORGANIZATION 3 | # FIRST AUTHOR , YEAR. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: PACKAGE VERSION\n" 8 | "POT-Creation-Date: 2021-05-28 15:15+0200\n" 9 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 10 | "Last-Translator: FULL NAME \n" 11 | "Language-Team: LANGUAGE \n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Generated-By: pygettext.py 1.5\n" 16 | 17 | 18 | #: gw2rpc/character.py:6 19 | msgid "Guardian" 20 | msgstr "" 21 | 22 | #: gw2rpc/character.py:7 23 | msgid "Warrior" 24 | msgstr "" 25 | 26 | #: gw2rpc/character.py:8 27 | msgid "Engineer" 28 | msgstr "" 29 | 30 | #: gw2rpc/character.py:9 31 | msgid "Ranger" 32 | msgstr "" 33 | 34 | #: gw2rpc/character.py:10 35 | msgid "Thief" 36 | msgstr "" 37 | 38 | #: gw2rpc/character.py:11 39 | msgid "Elementalist" 40 | msgstr "" 41 | 42 | #: gw2rpc/character.py:12 43 | msgid "Mesmer" 44 | msgstr "" 45 | 46 | #: gw2rpc/character.py:13 47 | msgid "Necromancer" 48 | msgstr "" 49 | 50 | #: gw2rpc/character.py:14 51 | msgid "Revenant" 52 | msgstr "" 53 | 54 | #: gw2rpc/character.py:17 55 | msgid "Asura" 56 | msgstr "" 57 | 58 | #: gw2rpc/character.py:17 59 | msgid "Charr" 60 | msgstr "" 61 | 62 | #: gw2rpc/character.py:17 63 | msgid "Human" 64 | msgstr "" 65 | 66 | #: gw2rpc/character.py:17 67 | msgid "Norn" 68 | msgstr "" 69 | 70 | #: gw2rpc/character.py:17 71 | msgid "Sylvari" 72 | msgstr "" 73 | 74 | #: gw2rpc/character.py:20 75 | msgid "Druid" 76 | msgstr "" 77 | 78 | #: gw2rpc/character.py:21 79 | msgid "Daredevil" 80 | msgstr "" 81 | 82 | #: gw2rpc/character.py:22 83 | msgid "Berserker" 84 | msgstr "" 85 | 86 | #: gw2rpc/character.py:23 87 | msgid "Dragonhunter" 88 | msgstr "" 89 | 90 | #: gw2rpc/character.py:24 91 | msgid "Reaper" 92 | msgstr "" 93 | 94 | #: gw2rpc/character.py:25 95 | msgid "Chronomancer" 96 | msgstr "" 97 | 98 | #: gw2rpc/character.py:26 99 | msgid "Scrapper" 100 | msgstr "" 101 | 102 | #: gw2rpc/character.py:27 103 | msgid "Tempest" 104 | msgstr "" 105 | 106 | #: gw2rpc/character.py:28 107 | msgid "Herald" 108 | msgstr "" 109 | 110 | #: gw2rpc/character.py:29 111 | msgid "Soulbeast" 112 | msgstr "" 113 | 114 | #: gw2rpc/character.py:30 115 | msgid "Weaver" 116 | msgstr "" 117 | 118 | #: gw2rpc/character.py:31 119 | msgid "Holosmith" 120 | msgstr "" 121 | 122 | #: gw2rpc/character.py:32 123 | msgid "Deadeye" 124 | msgstr "" 125 | 126 | #: gw2rpc/character.py:33 127 | msgid "Mirage" 128 | msgstr "" 129 | 130 | #: gw2rpc/character.py:34 131 | msgid "Scourge" 132 | msgstr "" 133 | 134 | #: gw2rpc/character.py:35 135 | msgid "Spellbreaker" 136 | msgstr "" 137 | 138 | #: gw2rpc/character.py:36 139 | msgid "Firebrand" 140 | msgstr "" 141 | 142 | #: gw2rpc/character.py:37 143 | msgid "Renegade" 144 | msgstr "" 145 | 146 | #: gw2rpc/gw2rpc.py:39 147 | msgid "Anvil Rock" 148 | msgstr "" 149 | 150 | #: gw2rpc/gw2rpc.py:39 151 | msgid "Blackgate" 152 | msgstr "" 153 | 154 | #: gw2rpc/gw2rpc.py:39 155 | msgid "Borlis Pass" 156 | msgstr "" 157 | 158 | #: gw2rpc/gw2rpc.py:39 159 | msgid "Crystal Desert" 160 | msgstr "" 161 | 162 | #: gw2rpc/gw2rpc.py:40 163 | msgid "Darkhaven" 164 | msgstr "" 165 | 166 | #: gw2rpc/gw2rpc.py:40 167 | msgid "Devona's Rest" 168 | msgstr "" 169 | 170 | #: gw2rpc/gw2rpc.py:40 171 | msgid "Dragonbrand" 172 | msgstr "" 173 | 174 | #: gw2rpc/gw2rpc.py:40 175 | msgid "Ehmry Bay" 176 | msgstr "" 177 | 178 | #: gw2rpc/gw2rpc.py:41 179 | msgid "Eredon Terrace" 180 | msgstr "" 181 | 182 | #: gw2rpc/gw2rpc.py:41 183 | msgid "Ferguson's Crossing" 184 | msgstr "" 185 | 186 | #: gw2rpc/gw2rpc.py:41 187 | msgid "Fort Aspenwood" 188 | msgstr "" 189 | 190 | #: gw2rpc/gw2rpc.py:42 191 | msgid "Gate of Madness" 192 | msgstr "" 193 | 194 | #: gw2rpc/gw2rpc.py:42 195 | msgid "Henge of Denravi" 196 | msgstr "" 197 | 198 | #: gw2rpc/gw2rpc.py:42 199 | msgid "Isle of Janthir" 200 | msgstr "" 201 | 202 | #: gw2rpc/gw2rpc.py:43 203 | msgid "Jade Quarry" 204 | msgstr "" 205 | 206 | #: gw2rpc/gw2rpc.py:43 207 | msgid "Kaineng" 208 | msgstr "" 209 | 210 | #: gw2rpc/gw2rpc.py:43 211 | msgid "Maguuma" 212 | msgstr "" 213 | 214 | #: gw2rpc/gw2rpc.py:43 215 | msgid "Northern Shiverpeaks" 216 | msgstr "" 217 | 218 | #: gw2rpc/gw2rpc.py:44 219 | msgid "Sanctum of Rall" 220 | msgstr "" 221 | 222 | #: gw2rpc/gw2rpc.py:44 223 | msgid "Sea of Sorrows" 224 | msgstr "" 225 | 226 | #: gw2rpc/gw2rpc.py:44 227 | msgid "Sorrow's Furnace" 228 | msgstr "" 229 | 230 | #: gw2rpc/gw2rpc.py:45 231 | msgid "Stormbluff Isle" 232 | msgstr "" 233 | 234 | #: gw2rpc/gw2rpc.py:45 235 | msgid "Tarnished Coast" 236 | msgstr "" 237 | 238 | #: gw2rpc/gw2rpc.py:45 239 | msgid "Yak's Bend" 240 | msgstr "" 241 | 242 | #: gw2rpc/gw2rpc.py:48 243 | msgid "Aurora Glade" 244 | msgstr "" 245 | 246 | #: gw2rpc/gw2rpc.py:48 247 | msgid "Blacktide" 248 | msgstr "" 249 | 250 | #: gw2rpc/gw2rpc.py:48 251 | msgid "Desolation" 252 | msgstr "" 253 | 254 | #: gw2rpc/gw2rpc.py:48 255 | msgid "Far Shiverpeaks" 256 | msgstr "" 257 | 258 | #: gw2rpc/gw2rpc.py:49 259 | msgid "Fissure of Woe" 260 | msgstr "" 261 | 262 | #: gw2rpc/gw2rpc.py:49 263 | msgid "Gandara" 264 | msgstr "" 265 | 266 | #: gw2rpc/gw2rpc.py:49 267 | msgid "Gunnar's Hold" 268 | msgstr "" 269 | 270 | #: gw2rpc/gw2rpc.py:49 271 | msgid "Piken Square" 272 | msgstr "" 273 | 274 | #: gw2rpc/gw2rpc.py:50 275 | msgid "Ring of Fire" 276 | msgstr "" 277 | 278 | #: gw2rpc/gw2rpc.py:50 279 | msgid "Ruins of Surmia" 280 | msgstr "" 281 | 282 | #: gw2rpc/gw2rpc.py:50 283 | msgid "Seafarer's Rest" 284 | msgstr "" 285 | 286 | #: gw2rpc/gw2rpc.py:50 287 | msgid "Underworld" 288 | msgstr "" 289 | 290 | #: gw2rpc/gw2rpc.py:51 291 | msgid "Arborstone [FR]" 292 | msgstr "" 293 | 294 | #: gw2rpc/gw2rpc.py:51 295 | msgid "Augury Rock [FR]" 296 | msgstr "" 297 | 298 | #: gw2rpc/gw2rpc.py:51 299 | msgid "Vabbi" 300 | msgstr "" 301 | 302 | #: gw2rpc/gw2rpc.py:51 303 | msgid "Whiteside Ridge" 304 | msgstr "" 305 | 306 | #: gw2rpc/gw2rpc.py:52 307 | msgid "Fort Ranik [FR]" 308 | msgstr "" 309 | 310 | #: gw2rpc/gw2rpc.py:52 311 | msgid "Jade Sea [FR]" 312 | msgstr "" 313 | 314 | #: gw2rpc/gw2rpc.py:52 315 | msgid "Vizunah Square [FR]" 316 | msgstr "" 317 | 318 | #: gw2rpc/gw2rpc.py:53 319 | msgid "Abaddon's Mouth [DE]" 320 | msgstr "" 321 | 322 | #: gw2rpc/gw2rpc.py:53 323 | msgid "Drakkar Lake [DE]" 324 | msgstr "" 325 | 326 | #: gw2rpc/gw2rpc.py:53 327 | msgid "Dzagonur [DE]" 328 | msgstr "" 329 | 330 | #: gw2rpc/gw2rpc.py:54 331 | msgid "Elona Reach [DE]" 332 | msgstr "" 333 | 334 | #: gw2rpc/gw2rpc.py:54 335 | msgid "Kodash [DE]" 336 | msgstr "" 337 | 338 | #: gw2rpc/gw2rpc.py:54 339 | msgid "Miller's Sound [DE]" 340 | msgstr "" 341 | 342 | #: gw2rpc/gw2rpc.py:55 343 | msgid "Baruch Bay [SP]" 344 | msgstr "" 345 | 346 | #: gw2rpc/gw2rpc.py:55 347 | msgid "Riverside [DE]" 348 | msgstr "" 349 | 350 | #: gw2rpc/gw2rpc.py:92 351 | msgid "About" 352 | msgstr "" 353 | 354 | #: gw2rpc/gw2rpc.py:94 355 | msgid "Join support server" 356 | msgstr "" 357 | 358 | #: gw2rpc/gw2rpc.py:97 359 | msgid "Guild Wars 2 with Discord" 360 | msgstr "" 361 | 362 | #: gw2rpc/gw2rpc.py:137 363 | msgid "Could not check for updates - check your connection!" 364 | msgstr "" 365 | 366 | #: gw2rpc/gw2rpc.py:143 367 | msgid "There is a new update for GW2 Rich Presence available. Would you like to be taken to the download page now?" 368 | msgstr "" 369 | 370 | #: gw2rpc/gw2rpc.py:165 371 | msgid "Fractals of the Mists" 372 | msgstr "" 373 | 374 | #: gw2rpc/gw2rpc.py:170 375 | msgid "fractal" 376 | msgstr "" 377 | 378 | #: gw2rpc/gw2rpc.py:198 gw2rpc/gw2rpc.py:199 379 | msgid "in " 380 | msgstr "" 381 | 382 | #: gw2rpc/gw2rpc.py:214 383 | msgid "fighting " 384 | msgstr "" 385 | 386 | #: gw2rpc/gw2rpc.py:216 387 | msgid "completing " 388 | msgstr "" 389 | 390 | #: gw2rpc/gw2rpc.py:285 391 | msgid " near " 392 | msgstr "" 393 | 394 | #: gw2rpc/gw2rpc.py:302 395 | msgid "in character selection" 396 | msgstr "" 397 | 398 | #: gw2rpc/gw2rpc.py:307 399 | msgid "Character Selection" 400 | msgstr "" 401 | 402 | 403 | msgid "Mistlock Observatory" 404 | msgstr "" 405 | 406 | msgid "Uncategorized" 407 | msgstr "" 408 | 409 | msgid "Snowblind" 410 | msgstr "" 411 | 412 | msgid "Swampland" 413 | msgstr "" 414 | 415 | msgid "Urban Battleground" 416 | msgstr "" 417 | 418 | msgid "Urban Battleground" 419 | msgstr "" 420 | 421 | msgid "Siren's Reef" 422 | msgstr "" 423 | 424 | msgid "Aquatic Ruins" 425 | msgstr "" 426 | 427 | msgid "Cliffside" 428 | msgstr "" 429 | 430 | msgid "Underground Facility" 431 | msgstr "" 432 | 433 | msgid "Volcanic" 434 | msgstr "" 435 | 436 | msgid "Molten Furnace" 437 | msgstr "" 438 | 439 | msgid "Aetherblade" 440 | msgstr "" 441 | 442 | msgid "Thaumanova Reactor" 443 | msgstr "" 444 | 445 | msgid "Solid Ocean" 446 | msgstr "" 447 | 448 | msgid "Molten Boss" 449 | msgstr "" 450 | 451 | msgid "Mai Trin" 452 | msgstr "" 453 | 454 | msgid "Chaos" 455 | msgstr "" 456 | 457 | msgid "Nightmare" 458 | msgstr "" 459 | 460 | msgid "Shattered Observatory" 461 | msgstr "" 462 | 463 | msgid "Twilight Oasis" 464 | msgstr "" 465 | 466 | msgid "Deepstone" 467 | msgstr "" 468 | 469 | msgid "Sunqua Peak" 470 | msgstr "" 471 | 472 | msgid "Skorvald" 473 | msgstr "" 474 | 475 | msgid "Ensolyss" 476 | msgstr "" 477 | 478 | msgid "Ai" 479 | msgstr "" 480 | 481 | msgid "Artsariiv" 482 | msgstr "" 483 | 484 | msgid "Arkk" 485 | msgstr "" 486 | 487 | msgid "M.A.M.A." 488 | msgstr "" 489 | 490 | msgid "Siax" 491 | msgstr "" 492 | 493 | msgid "Vale Guardian" 494 | msgstr "" 495 | 496 | msgid "Spirit Woods" 497 | msgstr "" 498 | 499 | msgid "Gorseval" 500 | msgstr "" 501 | 502 | msgid "Sabetha" 503 | msgstr "" 504 | 505 | msgid "Slothasor" 506 | msgstr "" 507 | 508 | msgid "Bandit Trio" 509 | msgstr "" 510 | 511 | msgid "Matthias" 512 | msgstr "" 513 | 514 | msgid "Escort" 515 | msgstr "" 516 | 517 | msgid "Keep Construct" 518 | msgstr "" 519 | 520 | msgid "Xera" 521 | msgstr "" 522 | 523 | msgid "Twisted Castle" 524 | msgstr "" 525 | 526 | msgid "Cairn" 527 | msgstr "" 528 | 529 | msgid "Mursaat Overseer" 530 | msgstr "" 531 | 532 | msgid "Samarog" 533 | msgstr "" 534 | 535 | msgid "Deimos" 536 | msgstr "" 537 | 538 | msgid "Soulless Horror" 539 | msgstr "" 540 | 541 | msgid "River of Souls" 542 | msgstr "" 543 | 544 | msgid "Broken King" 545 | msgstr "" 546 | 547 | msgid "Eater of Souls" 548 | msgstr "" 549 | 550 | msgid "Dhuum" 551 | msgstr "" 552 | 553 | msgid "Statue of Darkness" 554 | msgstr "" 555 | 556 | msgid "Conjured Amalgamate" 557 | msgstr "" 558 | 559 | msgid "Twin Largos" 560 | msgstr "" 561 | 562 | msgid "Qadim" 563 | msgstr "" 564 | 565 | msgid "Cardinal Sabir" 566 | msgstr "" 567 | 568 | msgid "Cardinal Adina" 569 | msgstr "" 570 | 571 | msgid "Qadim the Peerless" 572 | msgstr "" 573 | 574 | msgid "Harbinger" 575 | msgstr "" 576 | 577 | msgid "Willbender" 578 | msgstr "" 579 | 580 | msgid "Virtuoso" 581 | msgstr "" 582 | 583 | msgid "Catalyst" 584 | msgstr "" 585 | 586 | msgid "Bladesworn" 587 | msgstr "" 588 | 589 | msgid "Vindicator" 590 | msgstr "" 591 | 592 | msgid "Mechanist" 593 | msgstr "" 594 | 595 | msgid "Specter" 596 | msgstr "" 597 | 598 | msgid "Untamed" 599 | msgstr "" 600 | 601 | msgid "Commander" 602 | msgstr "" -------------------------------------------------------------------------------- /locales/es/LC_MESSAGES/base.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR ORGANIZATION 3 | # FIRST AUTHOR , YEAR. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: PACKAGE VERSION\n" 8 | "POT-Creation-Date: 2021-05-28 15:15+0200\n" 9 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 10 | "Last-Translator: FULL NAME \n" 11 | "Language-Team: LANGUAGE \n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Generated-By: pygettext.py 1.5\n" 16 | 17 | 18 | #: gw2rpc/character.py:6 19 | msgid "Guardian" 20 | msgstr "Guardián" 21 | 22 | #: gw2rpc/character.py:7 23 | msgid "Warrior" 24 | msgstr "Guerrero" 25 | 26 | #: gw2rpc/character.py:8 27 | msgid "Engineer" 28 | msgstr "Ingeniero" 29 | 30 | #: gw2rpc/character.py:9 31 | msgid "Ranger" 32 | msgstr "Guardabosques" 33 | 34 | #: gw2rpc/character.py:10 35 | msgid "Thief" 36 | msgstr "Ladrón" 37 | 38 | #: gw2rpc/character.py:11 39 | msgid "Elementalist" 40 | msgstr "Elementalista" 41 | 42 | #: gw2rpc/character.py:12 43 | msgid "Mesmer" 44 | msgstr "Hipnotizador" 45 | 46 | #: gw2rpc/character.py:13 47 | msgid "Necromancer" 48 | msgstr "Nigromante" 49 | 50 | #: gw2rpc/character.py:14 51 | msgid "Revenant" 52 | msgstr "Retornado" 53 | 54 | #: gw2rpc/character.py:17 55 | msgid "Asura" 56 | msgstr "Asura" 57 | 58 | #: gw2rpc/character.py:17 59 | msgid "Charr" 60 | msgstr "Charr" 61 | 62 | #: gw2rpc/character.py:17 63 | msgid "Human" 64 | msgstr "Humano" 65 | 66 | #: gw2rpc/character.py:17 67 | msgid "Norn" 68 | msgstr "Norn" 69 | 70 | #: gw2rpc/character.py:17 71 | msgid "Sylvari" 72 | msgstr "Sylvari" 73 | 74 | #: gw2rpc/character.py:20 75 | msgid "Druid" 76 | msgstr "Druida" 77 | 78 | #: gw2rpc/character.py:21 79 | msgid "Daredevil" 80 | msgstr "Temerario" 81 | 82 | #: gw2rpc/character.py:22 83 | msgid "Berserker" 84 | msgstr "Berserker" 85 | 86 | #: gw2rpc/character.py:23 87 | msgid "Dragonhunter" 88 | msgstr "Cazadragones" 89 | 90 | #: gw2rpc/character.py:24 91 | msgid "Reaper" 92 | msgstr "Segador" 93 | 94 | #: gw2rpc/character.py:25 95 | msgid "Chronomancer" 96 | msgstr "Cronomante" 97 | 98 | #: gw2rpc/character.py:26 99 | msgid "Scrapper" 100 | msgstr "Chatarrero" 101 | 102 | #: gw2rpc/character.py:27 103 | msgid "Tempest" 104 | msgstr "Tempestad" 105 | 106 | #: gw2rpc/character.py:28 107 | msgid "Herald" 108 | msgstr "Heraldo" 109 | 110 | #: gw2rpc/character.py:29 111 | msgid "Soulbeast" 112 | msgstr "Bestialma" 113 | 114 | #: gw2rpc/character.py:30 115 | msgid "Weaver" 116 | msgstr "Tejedor" 117 | 118 | #: gw2rpc/character.py:31 119 | msgid "Holosmith" 120 | msgstr "Holoartesano" 121 | 122 | #: gw2rpc/character.py:32 123 | msgid "Deadeye" 124 | msgstr "Certero" 125 | 126 | #: gw2rpc/character.py:33 127 | msgid "Mirage" 128 | msgstr "Quimérico" 129 | 130 | #: gw2rpc/character.py:34 131 | msgid "Scourge" 132 | msgstr "Azotador" 133 | 134 | #: gw2rpc/character.py:35 135 | msgid "Spellbreaker" 136 | msgstr "Rompehechizos" 137 | 138 | #: gw2rpc/character.py:36 139 | msgid "Firebrand" 140 | msgstr "Abrasador" 141 | 142 | #: gw2rpc/character.py:37 143 | msgid "Renegade" 144 | msgstr "Renegado" 145 | 146 | #: gw2rpc/gw2rpc.py:39 147 | msgid "Anvil Rock" 148 | msgstr "Roca del Augurio" 149 | 150 | #: gw2rpc/gw2rpc.py:39 151 | msgid "Blackgate" 152 | msgstr "Puertanegra" 153 | 154 | #: gw2rpc/gw2rpc.py:39 155 | msgid "Borlis Pass" 156 | msgstr "Paso de Borlis" 157 | 158 | #: gw2rpc/gw2rpc.py:39 159 | msgid "Crystal Desert" 160 | msgstr "Desierto de Cristal" 161 | 162 | #: gw2rpc/gw2rpc.py:40 163 | msgid "Darkhaven" 164 | msgstr "Refugio Oscuro" 165 | 166 | #: gw2rpc/gw2rpc.py:40 167 | msgid "Devona's Rest" 168 | msgstr "Descanso de Devona" 169 | 170 | #: gw2rpc/gw2rpc.py:40 171 | msgid "Dragonbrand" 172 | msgstr "Marca del Dragón" 173 | 174 | #: gw2rpc/gw2rpc.py:40 175 | msgid "Ehmry Bay" 176 | msgstr "Bahía de Ehmy" 177 | 178 | #: gw2rpc/gw2rpc.py:41 179 | msgid "Eredon Terrace" 180 | msgstr "Terraza de Eredon" 181 | 182 | #: gw2rpc/gw2rpc.py:41 183 | msgid "Ferguson's Crossing" 184 | msgstr "Encrucijada de Ferguson" 185 | 186 | #: gw2rpc/gw2rpc.py:41 187 | msgid "Fort Aspenwood" 188 | msgstr "Fuerte Aspenwood" 189 | 190 | #: gw2rpc/gw2rpc.py:42 191 | msgid "Gate of Madness" 192 | msgstr "Puerta de la Locura" 193 | 194 | #: gw2rpc/gw2rpc.py:42 195 | msgid "Henge of Denravi" 196 | msgstr "Círculo de Denravi" 197 | 198 | #: gw2rpc/gw2rpc.py:42 199 | msgid "Isle of Janthir" 200 | msgstr "Isla de Janthir" 201 | 202 | #: gw2rpc/gw2rpc.py:43 203 | msgid "Jade Quarry" 204 | msgstr "Cantera de Jade" 205 | 206 | #: gw2rpc/gw2rpc.py:43 207 | msgid "Kaineng" 208 | msgstr "Kaineng" 209 | 210 | #: gw2rpc/gw2rpc.py:43 211 | msgid "Maguuma" 212 | msgstr "Maguuma" 213 | 214 | #: gw2rpc/gw2rpc.py:43 215 | msgid "Northern Shiverpeaks" 216 | msgstr "Picosescalofriantes del Norte" 217 | 218 | #: gw2rpc/gw2rpc.py:44 219 | msgid "Sanctum of Rall" 220 | msgstr "Sagrario de Rall" 221 | 222 | #: gw2rpc/gw2rpc.py:44 223 | msgid "Sea of Sorrows" 224 | msgstr "Mar de los Pesares" 225 | 226 | #: gw2rpc/gw2rpc.py:44 227 | msgid "Sorrow's Furnace" 228 | msgstr "Fragua del Pesar" 229 | 230 | #: gw2rpc/gw2rpc.py:45 231 | msgid "Stormbluff Isle" 232 | msgstr "Isla Cimatormenta" 233 | 234 | #: gw2rpc/gw2rpc.py:45 235 | msgid "Tarnished Coast" 236 | msgstr "Costa de Bronce" 237 | 238 | #: gw2rpc/gw2rpc.py:45 239 | msgid "Yak's Bend" 240 | msgstr "Declive del Yak" 241 | 242 | #: gw2rpc/gw2rpc.py:48 243 | msgid "Aurora Glade" 244 | msgstr "Claro de la Aurora" 245 | 246 | #: gw2rpc/gw2rpc.py:48 247 | msgid "Blacktide" 248 | msgstr "Marea Negra" 249 | 250 | #: gw2rpc/gw2rpc.py:48 251 | msgid "Desolation" 252 | msgstr "Desolación" 253 | 254 | #: gw2rpc/gw2rpc.py:48 255 | msgid "Far Shiverpeaks" 256 | msgstr "Picosescalofriantes Lejanas" 257 | 258 | #: gw2rpc/gw2rpc.py:49 259 | msgid "Fissure of Woe" 260 | msgstr "Fisura de la Aflicción" 261 | 262 | #: gw2rpc/gw2rpc.py:49 263 | msgid "Gandara" 264 | msgstr "Gandara" 265 | 266 | #: gw2rpc/gw2rpc.py:49 267 | msgid "Gunnar's Hold" 268 | msgstr "Baluarte de Gonnar" 269 | 270 | #: gw2rpc/gw2rpc.py:49 271 | msgid "Piken Square" 272 | msgstr "Plaza de Piken" 273 | 274 | #: gw2rpc/gw2rpc.py:50 275 | msgid "Ring of Fire" 276 | msgstr "Anillo de Fuego" 277 | 278 | #: gw2rpc/gw2rpc.py:50 279 | msgid "Ruins of Surmia" 280 | msgstr "Ruinas de Surmia" 281 | 282 | #: gw2rpc/gw2rpc.py:50 283 | msgid "Seafarer's Rest" 284 | msgstr "Refugio del Viajante" 285 | 286 | #: gw2rpc/gw2rpc.py:50 287 | msgid "Underworld" 288 | msgstr "Inframundo" 289 | 290 | #: gw2rpc/gw2rpc.py:51 291 | msgid "Arborstone [FR]" 292 | msgstr "Piedra Arbórea [FR]" 293 | 294 | #: gw2rpc/gw2rpc.py:51 295 | msgid "Augury Rock [FR]" 296 | msgstr "Roca del Augurio [FR]" 297 | 298 | #: gw2rpc/gw2rpc.py:51 299 | msgid "Vabbi" 300 | msgstr "Vabbi" 301 | 302 | #: gw2rpc/gw2rpc.py:51 303 | msgid "Whiteside Ridge" 304 | msgstr "Cadena Laderablanca" 305 | 306 | #: gw2rpc/gw2rpc.py:52 307 | msgid "Fort Ranik [FR]" 308 | msgstr "Fuerte de Ranik [FR]" 309 | 310 | #: gw2rpc/gw2rpc.py:52 311 | msgid "Jade Sea [FR]" 312 | msgstr "Mar de Jade [FR]" 313 | 314 | #: gw2rpc/gw2rpc.py:52 315 | msgid "Vizunah Square [FR]" 316 | msgstr "Plaza de Vizunah [FR]" 317 | 318 | #: gw2rpc/gw2rpc.py:53 319 | msgid "Abaddon's Mouth [DE]" 320 | msgstr "Boca de Abaddon [DE]" 321 | 322 | #: gw2rpc/gw2rpc.py:53 323 | msgid "Drakkar Lake [DE]" 324 | msgstr "Lago de Drakkar [DE]" 325 | 326 | #: gw2rpc/gw2rpc.py:53 327 | msgid "Dzagonur [DE]" 328 | msgstr "Dzagonur [DE]" 329 | 330 | #: gw2rpc/gw2rpc.py:54 331 | msgid "Elona Reach [DE]" 332 | msgstr "Cañón de Elona [DE]" 333 | 334 | #: gw2rpc/gw2rpc.py:54 335 | msgid "Kodash [DE]" 336 | msgstr "Kodash [DE]" 337 | 338 | #: gw2rpc/gw2rpc.py:54 339 | msgid "Miller's Sound [DE]" 340 | msgstr "Sonido de Miller [DE]" 341 | 342 | #: gw2rpc/gw2rpc.py:55 343 | msgid "Baruch Bay [SP]" 344 | msgstr "Bahía de Baruch [ES]" 345 | 346 | #: gw2rpc/gw2rpc.py:55 347 | msgid "Riverside [DE]" 348 | msgstr "Ribera [DE]" 349 | 350 | #: gw2rpc/gw2rpc.py:92 351 | msgid "About" 352 | msgstr "Acerca de" 353 | 354 | #: gw2rpc/gw2rpc.py:94 355 | msgid "Join support server" 356 | msgstr "Servidor de soporte" 357 | 358 | #: gw2rpc/gw2rpc.py:97 359 | msgid "Guild Wars 2 with Discord" 360 | msgstr "Guild Wars 2 con Discord" 361 | 362 | #: gw2rpc/gw2rpc.py:137 363 | msgid "Could not check for updates - check your connection!" 364 | msgstr "No se han podido buscar actualizaciones - ¡Comprueba tu conexión!" 365 | 366 | #: gw2rpc/gw2rpc.py:143 367 | msgid "There is a new update for GW2 Rich Presence available. Would you like to be taken to the download page now?" 368 | msgstr "Hay una nueva actualización para GW2 Rich Presence disponible. ¿Quieres abrir la página de descarga ahora?" 369 | 370 | #: gw2rpc/gw2rpc.py:165 371 | msgid "Fractals of the Mists" 372 | msgstr "Fractales de la Niebla" 373 | 374 | #: gw2rpc/gw2rpc.py:170 375 | msgid "fractal" 376 | msgstr "fractal" 377 | 378 | #: gw2rpc/gw2rpc.py:198 gw2rpc/gw2rpc.py:199 379 | msgid "in " 380 | msgstr "en " 381 | 382 | #: gw2rpc/gw2rpc.py:214 383 | msgid "fighting " 384 | msgstr "luchando " 385 | 386 | #: gw2rpc/gw2rpc.py:216 387 | msgid "completing " 388 | msgstr "completando " 389 | 390 | #: gw2rpc/gw2rpc.py:285 391 | msgid " near " 392 | msgstr " cerca " 393 | 394 | #: gw2rpc/gw2rpc.py:302 395 | msgid "in character selection" 396 | msgstr "en selección de personaje" 397 | 398 | #: gw2rpc/gw2rpc.py:307 399 | msgid "Character Selection" 400 | msgstr "Selección de Personaje" 401 | 402 | 403 | msgid "Mistlock Observatory" 404 | msgstr "Observatorio del Bloqueo de la Niebla" 405 | 406 | msgid "Uncategorized" 407 | msgstr "Sin Clasificar" 408 | 409 | msgid "Snowblind" 410 | msgstr "Ceguera de la Nieve" 411 | 412 | msgid "Swampland" 413 | msgstr "Cenagal" 414 | 415 | msgid "Urban Battleground" 416 | msgstr "Campo de Batalla Urbano" 417 | 418 | msgid "Aquatic Ruins" 419 | msgstr "Ruinas Acuáticas" 420 | 421 | msgid "Siren's Reef" 422 | msgstr "Arrecife de la Sirena" 423 | 424 | msgid "Cliffside" 425 | msgstr "Despeñadero" 426 | 427 | msgid "Underground Facility" 428 | msgstr "Instalación Subterránea" 429 | 430 | msgid "Volcanic" 431 | msgstr "Volcánico" 432 | 433 | msgid "Molten Furnace" 434 | msgstr "Fragua Fundida" 435 | 436 | msgid "Aetherblade" 437 | msgstr "Filoetéreo" 438 | 439 | msgid "Thaumanova Reactor" 440 | msgstr "Reactor Taumanova" 441 | 442 | msgid "Solid Ocean" 443 | msgstr "Océano Sólido" 444 | 445 | msgid "Molten Boss" 446 | msgstr "Jefes Fundidos" 447 | 448 | msgid "Mai Trin" 449 | msgstr "Mai Trin" 450 | 451 | msgid "Chaos" 452 | msgstr "Caos" 453 | 454 | msgid "Nightmare" 455 | msgstr "Pesadilla" 456 | 457 | msgid "Shattered Observatory" 458 | msgstr "Observatorio Asolado" 459 | 460 | msgid "Twilight Oasis" 461 | msgstr "Oasis del Crepúsculo" 462 | 463 | msgid "Deepstone" 464 | msgstr "Rocahonda" 465 | 466 | msgid "Sunqua Peak" 467 | msgstr "Pico de Sunqua" 468 | 469 | msgid "Skorvald" 470 | msgstr "Skorvald" 471 | 472 | msgid "Ensolyss" 473 | msgstr "" 474 | 475 | msgid "Ai" 476 | msgstr "" 477 | 478 | msgid "Artsariiv" 479 | msgstr "" 480 | 481 | msgid "Arkk" 482 | msgstr "" 483 | 484 | msgid "M.A.M.A." 485 | msgstr "" 486 | 487 | msgid "Siax" 488 | msgstr "Siax" 489 | 490 | msgid "Vale Guardian" 491 | msgstr "Guardián del valle" 492 | 493 | msgid "Spirit Woods" 494 | msgstr "Bosques espirituales" 495 | 496 | msgid "Gorseval" 497 | msgstr "" 498 | 499 | msgid "Sabetha" 500 | msgstr "" 501 | 502 | msgid "Slothasor" 503 | msgstr "Perezón" 504 | 505 | msgid "Bandit Trio" 506 | msgstr "Campamento de prisioneros" 507 | 508 | msgid "Matthias" 509 | msgstr "Matías Gabrel" 510 | 511 | msgid "Escort" 512 | msgstr "Asedio a la fortaleza" 513 | 514 | msgid "Keep Construct" 515 | msgstr "Ensamblaje de la fortaleza" 516 | 517 | msgid "Xera" 518 | msgstr "" 519 | 520 | msgid "Twisted Castle" 521 | msgstr "Castillo Retorcido" 522 | 523 | msgid "Cairn" 524 | msgstr "" 525 | 526 | msgid "Mursaat Overseer" 527 | msgstr "Dirigente mursaat" 528 | 529 | msgid "Samarog" 530 | msgstr "" 531 | 532 | msgid "Deimos" 533 | msgstr "" 534 | 535 | msgid "Soulless Horror" 536 | msgstr "Horror sin alma" 537 | 538 | msgid "River of Souls" 539 | msgstr "Río de almas" 540 | 541 | msgid "Broken King" 542 | msgstr "Rey Roto" 543 | 544 | msgid "Eater of Souls" 545 | msgstr "Devorador de almas" 546 | 547 | msgid "Dhuum" 548 | msgstr "" 549 | 550 | msgid "Statue of Darkness" 551 | msgstr "Estatua de Oscuridad" 552 | 553 | msgid "Conjured Amalgamate" 554 | msgstr "Amalgamado conjurado" 555 | 556 | msgid "Twin Largos" 557 | msgstr "Largos gemelos" 558 | 559 | msgid "Qadim" 560 | msgstr "" 561 | 562 | msgid "Cardinal Sabir" 563 | msgstr "Cardenal Sabir" 564 | 565 | msgid "Cardinal Adina" 566 | msgstr "Cardenal Adina" 567 | 568 | msgid "Qadim the Peerless" 569 | msgstr "Qadim el Simpar" 570 | 571 | msgid "Harbinger" 572 | msgstr "Augurador" 573 | 574 | msgid "Willbender" 575 | msgstr "Subyugador" 576 | 577 | msgid "Virtuoso" 578 | msgstr "Virtuoso" 579 | 580 | msgid "Catalyst" 581 | msgstr "Catalizador" 582 | 583 | msgid "Bladesworn" 584 | msgstr "Jurafilos" 585 | 586 | msgid "Vindicator" 587 | msgstr "Justiciero" 588 | 589 | msgid "Mechanist" 590 | msgstr "Mechanista" 591 | 592 | msgid "Specter" 593 | msgstr "Espectro" 594 | 595 | msgid "Untamed" 596 | msgstr "Indómito" 597 | 598 | msgid "Commander" 599 | msgstr "Comandante" 600 | 601 | msgid "jackal" 602 | msgstr "chacal" 603 | 604 | msgid "griffon" 605 | msgstr "grifo" 606 | 607 | msgid "springer" 608 | msgstr "saltarín" 609 | 610 | msgid "skimmer" 611 | msgstr "mantarraya" 612 | 613 | msgid "raptor" 614 | msgstr "raptor" 615 | 616 | msgid "roller beetle" 617 | msgstr "escarabajo" 618 | 619 | msgid "warclaw" 620 | msgstr "garrabélica" 621 | 622 | msgid "skyscale" 623 | msgstr "escamaceleste" 624 | 625 | msgid "siege turtle" 626 | msgstr "tortuga de asedio" 627 | 628 | msgid "on" 629 | msgstr "en" 630 | 631 | msgid "Whisper of Jormag" 632 | msgstr "Susurro de Jormag" 633 | 634 | msgid "Boneskinner" 635 | msgstr "Pelahuesos" 636 | 637 | msgid "Fraenir of Jormag" 638 | msgstr "Fraenir de Jormag" 639 | 640 | msgid "Voice And Claw of the Fallen" 641 | msgstr "Voz y Garra de los Caídos" 642 | 643 | msgid "Legendary Icebrood Construct" 644 | msgstr "Ensamblaje de la progelie" 645 | 646 | msgid "Mistlock Sanctuary" 647 | msgstr "Santuario del Bloqueo de la Niebla" 648 | 649 | msgid "Closest" 650 | msgstr "" 651 | 652 | # Molten boss fractal 653 | msgid "Berserker and Firestorm" 654 | msgstr "Berserker y Tormenta de fuego" 655 | 656 | # Sirens reef fractal 657 | msgid "Blasting Black Peter" 658 | msgstr "El Negro Peter Descargas" 659 | 660 | # Sirens reef fractal 661 | msgid "Arabella Crowe" 662 | msgstr "Capitana Crowe" 663 | 664 | msgid "Brood Queen" 665 | msgstr "Reine des couvées" 666 | 667 | msgid "Deepstone Sentinel" 668 | msgstr "Sentinelle de la Roche des abysses" 669 | 670 | msgid "The Voice" 671 | msgstr "La Voix" 672 | 673 | msgid "Bloomhunger" 674 | msgstr "Devoraflores" 675 | 676 | msgid "Jade Maw" 677 | msgstr "Fauces de jade" 678 | 679 | msgid "loading screen" 680 | msgstr "cargando pantalla" 681 | 682 | msgid "GW2RPC Raid Announcer" 683 | msgstr "" 684 | 685 | msgid "tagged up" 686 | msgstr "" 687 | 688 | msgid "Copy and paste the following to join" 689 | msgstr "" 690 | 691 | msgid "Yes" 692 | msgstr "" 693 | 694 | msgid "No" 695 | msgstr "" 696 | 697 | msgid "Announce raids:" 698 | msgstr "" 699 | 700 | msgid "Closest PoI" 701 | msgstr "" 702 | 703 | msgid "skiff" 704 | msgstr "" 705 | 706 | msgid "Jade Bot" 707 | msgstr "robot de jade" 708 | 709 | msgid "Elemental Source" 710 | msgstr "elemental de hielo" 711 | 712 | msgid "Lornar Dragonseeker" 713 | msgstr "Lornarr Dragonseeker" 714 | 715 | msgid "Frizz" 716 | msgstr "Frizz" 717 | 718 | msgid "Captain Mai Trin" 719 | msgstr "Capitana Mai Trin" 720 | 721 | msgid "Anomaly" 722 | msgstr "Anomalía" 723 | 724 | msgid "Brazen Gladiator" 725 | msgstr "Gladiador descarado" 726 | 727 | msgid "Amala" 728 | msgstr "Amala" 729 | 730 | msgid "a sandbinder" 731 | msgstr "lancero del sol élite" 732 | 733 | 734 | msgid "Captain Ashym" 735 | msgstr "Capitán Ashym" 736 | 737 | msgid "Jellyfish Beast" 738 | msgstr "Bestia medusa" 739 | 740 | msgid "Archdiviner" 741 | msgstr "archiadivino" 742 | 743 | msgid "Rabsovich" 744 | msgstr "" 745 | 746 | msgid "Rampaging Ice Elemental or Dredge Powersuit" 747 | msgstr "Elemental de hielo violento o Traje draga" 748 | 749 | msgid "Grawl Shaman" 750 | msgstr "chamán grawl" 751 | 752 | msgid "Imbued Shaman" 753 | msgstr "chamán imbuido" 754 | 755 | msgid "in Weapon Testing Facility" 756 | msgstr "" 757 | 758 | msgid "Subject 6" 759 | msgstr "Sujeto 6" 760 | 761 | msgid "Thaumanova Anomaly" 762 | msgstr "anomalía de Taumanova" 763 | 764 | # pre-event of Wing 7 765 | msgid "gate" 766 | msgstr "" 767 | 768 | msgid "Minister Li" 769 | msgstr "" 770 | 771 | msgid "Old Tom" 772 | msgstr "Viejo Tom" 773 | 774 | msgid "Raving Asura" 775 | msgstr "asura enloquecidos" 776 | 777 | msgid "Voice of the Mountain" 778 | msgstr "Voz de la Montaña" 779 | 780 | msgid "Sorrow of the Mountain" 781 | msgstr "Dolor de la montaña" 782 | 783 | msgid "Fury of the Mountain" 784 | msgstr "Furia de la Montaña" 785 | 786 | msgid "Dragon Void" 787 | msgstr "El Vacío Dracónico" 788 | 789 | msgid "Silent Surf" 790 | msgstr "Oleaje Silencioso" -------------------------------------------------------------------------------- /locales/fr/LC_MESSAGES/base.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR ORGANIZATION 3 | # FIRST AUTHOR , YEAR. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: PACKAGE VERSION\n" 8 | "POT-Creation-Date: 2021-05-28 15:15+0200\n" 9 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 10 | "Last-Translator: FULL NAME \n" 11 | "Language-Team: LANGUAGE \n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Generated-By: pygettext.py 1.5\n" 16 | 17 | 18 | #: gw2rpc/character.py:6 19 | msgid "Guardian" 20 | msgstr "Gardien" 21 | 22 | #: gw2rpc/character.py:7 23 | msgid "Warrior" 24 | msgstr "Guerrier" 25 | 26 | #: gw2rpc/character.py:8 27 | msgid "Engineer" 28 | msgstr "Ingénieur" 29 | 30 | #: gw2rpc/character.py:9 31 | msgid "Ranger" 32 | msgstr "Rôdeur" 33 | 34 | #: gw2rpc/character.py:10 35 | msgid "Thief" 36 | msgstr "Voleur" 37 | 38 | #: gw2rpc/character.py:11 39 | msgid "Elementalist" 40 | msgstr "Elémentaliste" 41 | 42 | #: gw2rpc/character.py:12 43 | msgid "Mesmer" 44 | msgstr "Envouteur" 45 | 46 | #: gw2rpc/character.py:13 47 | msgid "Necromancer" 48 | msgstr "Nécromancien" 49 | 50 | #: gw2rpc/character.py:14 51 | msgid "Revenant" 52 | msgstr "Revenant" 53 | 54 | #: gw2rpc/character.py:17 55 | msgid "Asura" 56 | msgstr "Asura" 57 | 58 | #: gw2rpc/character.py:17 59 | msgid "Charr" 60 | msgstr "Charr" 61 | 62 | #: gw2rpc/character.py:17 63 | msgid "Human" 64 | msgstr "Humain" 65 | 66 | #: gw2rpc/character.py:17 67 | msgid "Norn" 68 | msgstr "Norn" 69 | 70 | #: gw2rpc/character.py:17 71 | msgid "Sylvari" 72 | msgstr "Sylvari" 73 | 74 | #: gw2rpc/character.py:20 75 | msgid "Druid" 76 | msgstr "Druide" 77 | 78 | #: gw2rpc/character.py:21 79 | msgid "Daredevil" 80 | msgstr "Fracasseur" 81 | 82 | #: gw2rpc/character.py:22 83 | msgid "Berserker" 84 | msgstr "Berserker" 85 | 86 | #: gw2rpc/character.py:23 87 | msgid "Dragonhunter" 88 | msgstr "Draconnier" 89 | 90 | #: gw2rpc/character.py:24 91 | msgid "Reaper" 92 | msgstr "Faucheur" 93 | 94 | #: gw2rpc/character.py:25 95 | msgid "Chronomancer" 96 | msgstr "Chronomancien" 97 | 98 | #: gw2rpc/character.py:26 99 | msgid "Scrapper" 100 | msgstr "Mécatronicien" 101 | 102 | #: gw2rpc/character.py:27 103 | msgid "Tempest" 104 | msgstr "Cataclyste" 105 | 106 | #: gw2rpc/character.py:28 107 | msgid "Herald" 108 | msgstr "Hérault" 109 | 110 | #: gw2rpc/character.py:29 111 | msgid "Soulbeast" 112 | msgstr "Animorphe" 113 | 114 | #: gw2rpc/character.py:30 115 | msgid "Weaver" 116 | msgstr "Tissesort" 117 | 118 | #: gw2rpc/character.py:31 119 | msgid "Holosmith" 120 | msgstr "Holographiste" 121 | 122 | #: gw2rpc/character.py:32 123 | msgid "Deadeye" 124 | msgstr "Sniper" 125 | 126 | #: gw2rpc/character.py:33 127 | msgid "Mirage" 128 | msgstr "Mirage" 129 | 130 | #: gw2rpc/character.py:34 131 | msgid "Scourge" 132 | msgstr "Fléau" 133 | 134 | #: gw2rpc/character.py:35 135 | msgid "Spellbreaker" 136 | msgstr "Brisesort" 137 | 138 | #: gw2rpc/character.py:36 139 | msgid "Firebrand" 140 | msgstr "Incendiaire" 141 | 142 | #: gw2rpc/character.py:37 143 | msgid "Renegade" 144 | msgstr "Renégat" 145 | 146 | #: gw2rpc/gw2rpc.py:39 147 | msgid "Anvil Rock" 148 | msgstr "Rocher de l'enclume" 149 | 150 | #: gw2rpc/gw2rpc.py:39 151 | msgid "Blackgate" 152 | msgstr "Portenoire" 153 | 154 | #: gw2rpc/gw2rpc.py:39 155 | msgid "Borlis Pass" 156 | msgstr "Passage de Borlis" 157 | 158 | #: gw2rpc/gw2rpc.py:39 159 | msgid "Crystal Desert" 160 | msgstr "Désert de cristal" 161 | 162 | #: gw2rpc/gw2rpc.py:40 163 | msgid "Darkhaven" 164 | msgstr "Refuge noir" 165 | 166 | #: gw2rpc/gw2rpc.py:40 167 | msgid "Devona's Rest" 168 | msgstr "Repos de Devona" 169 | 170 | #: gw2rpc/gw2rpc.py:40 171 | msgid "Dragonbrand" 172 | msgstr "Stigmate du dragon" 173 | 174 | #: gw2rpc/gw2rpc.py:40 175 | msgid "Ehmry Bay" 176 | msgstr "Baie d'Ehmry" 177 | 178 | #: gw2rpc/gw2rpc.py:41 179 | msgid "Eredon Terrace" 180 | msgstr "Plateau d'Eredon" 181 | 182 | #: gw2rpc/gw2rpc.py:41 183 | msgid "Ferguson's Crossing" 184 | msgstr "Croisée de Ferguson" 185 | 186 | #: gw2rpc/gw2rpc.py:41 187 | msgid "Fort Aspenwood" 188 | msgstr "Fort Trembleforêt" 189 | 190 | #: gw2rpc/gw2rpc.py:42 191 | msgid "Gate of Madness" 192 | msgstr "Porte de la folie" 193 | 194 | #: gw2rpc/gw2rpc.py:42 195 | msgid "Henge of Denravi" 196 | msgstr "Cromlech de Denravi" 197 | 198 | #: gw2rpc/gw2rpc.py:42 199 | msgid "Isle of Janthir" 200 | msgstr "Ile de Janthir" 201 | 202 | #: gw2rpc/gw2rpc.py:43 203 | msgid "Jade Quarry" 204 | msgstr "Carrière de jade" 205 | 206 | #: gw2rpc/gw2rpc.py:43 207 | msgid "Kaineng" 208 | msgstr "Kaineng" 209 | 210 | #: gw2rpc/gw2rpc.py:43 211 | msgid "Maguuma" 212 | msgstr "Maguuma" 213 | 214 | #: gw2rpc/gw2rpc.py:43 215 | msgid "Northern Shiverpeaks" 216 | msgstr "Cimefroides du nord" 217 | 218 | #: gw2rpc/gw2rpc.py:44 219 | msgid "Sanctum of Rall" 220 | msgstr "Sanctuaire de Rall" 221 | 222 | #: gw2rpc/gw2rpc.py:44 223 | msgid "Sea of Sorrows" 224 | msgstr "Mer des lamentations" 225 | 226 | #: gw2rpc/gw2rpc.py:44 227 | msgid "Sorrow's Furnace" 228 | msgstr "Fournaise des lamentations" 229 | 230 | #: gw2rpc/gw2rpc.py:45 231 | msgid "Stormbluff Isle" 232 | msgstr "Ile e la Falaise tumultueuse" 233 | 234 | #: gw2rpc/gw2rpc.py:45 235 | msgid "Tarnished Coast" 236 | msgstr "Côte ternie" 237 | 238 | #: gw2rpc/gw2rpc.py:45 239 | msgid "Yak's Bend" 240 | msgstr "Courbe du Yak" 241 | 242 | #: gw2rpc/gw2rpc.py:48 243 | msgid "Aurora Glade" 244 | msgstr "Clairière de l'aurore" 245 | 246 | #: gw2rpc/gw2rpc.py:48 247 | msgid "Blacktide" 248 | msgstr "Noirflot" 249 | 250 | #: gw2rpc/gw2rpc.py:48 251 | msgid "Desolation" 252 | msgstr "Désolation" 253 | 254 | #: gw2rpc/gw2rpc.py:48 255 | msgid "Far Shiverpeaks" 256 | msgstr "Lointaines Cimefroides" 257 | 258 | #: gw2rpc/gw2rpc.py:49 259 | msgid "Fissure of Woe" 260 | msgstr "Fissure du malheur" 261 | 262 | #: gw2rpc/gw2rpc.py:49 263 | msgid "Gandara" 264 | msgstr "Gandara" 265 | 266 | #: gw2rpc/gw2rpc.py:49 267 | msgid "Gunnar's Hold" 268 | msgstr "Campement de Gunnar" 269 | 270 | #: gw2rpc/gw2rpc.py:49 271 | msgid "Piken Square" 272 | msgstr "Place Piken" 273 | 274 | #: gw2rpc/gw2rpc.py:50 275 | msgid "Ring of Fire" 276 | msgstr "Cercle de feu" 277 | 278 | #: gw2rpc/gw2rpc.py:50 279 | msgid "Ruins of Surmia" 280 | msgstr "Ruines de Surmia" 281 | 282 | #: gw2rpc/gw2rpc.py:50 283 | msgid "Seafarer's Rest" 284 | msgstr "Repos du Marin" 285 | 286 | #: gw2rpc/gw2rpc.py:50 287 | msgid "Underworld" 288 | msgstr "Outre-monde" 289 | 290 | #: gw2rpc/gw2rpc.py:51 291 | msgid "Arborstone [FR]" 292 | msgstr "Pierre Arborea [FR]" 293 | 294 | #: gw2rpc/gw2rpc.py:51 295 | msgid "Augury Rock [FR]" 296 | msgstr "Roche de l'augure [FR]" 297 | 298 | #: gw2rpc/gw2rpc.py:51 299 | msgid "Vabbi" 300 | msgstr "Vabbi" 301 | 302 | #: gw2rpc/gw2rpc.py:51 303 | msgid "Whiteside Ridge" 304 | msgstr "Crête de Verseblanc" 305 | 306 | #: gw2rpc/gw2rpc.py:52 307 | msgid "Fort Ranik [FR]" 308 | msgstr "Fort Ranik [FR]" 309 | 310 | #: gw2rpc/gw2rpc.py:52 311 | msgid "Jade Sea [FR]" 312 | msgstr "Mer de Jade [FR]" 313 | 314 | #: gw2rpc/gw2rpc.py:52 315 | msgid "Vizunah Square [FR]" 316 | msgstr "Place de Vizunah [FR]" 317 | 318 | #: gw2rpc/gw2rpc.py:53 319 | msgid "Abaddon's Mouth [DE]" 320 | msgstr "Bouche d'Abaddon [DE]" 321 | 322 | #: gw2rpc/gw2rpc.py:53 323 | msgid "Drakkar Lake [DE]" 324 | msgstr "Lac Drakkar [DE]" 325 | 326 | #: gw2rpc/gw2rpc.py:53 327 | msgid "Dzagonur [DE]" 328 | msgstr "Dzagonur [DE]" 329 | 330 | #: gw2rpc/gw2rpc.py:54 331 | msgid "Elona Reach [DE]" 332 | msgstr "Bief d'Elona [DE]" 333 | 334 | #: gw2rpc/gw2rpc.py:54 335 | msgid "Kodash [DE]" 336 | msgstr "Kodash [DE]" 337 | 338 | #: gw2rpc/gw2rpc.py:54 339 | msgid "Miller's Sound [DE]" 340 | msgstr "Détroit de Miller [DE]" 341 | 342 | #: gw2rpc/gw2rpc.py:55 343 | msgid "Baruch Bay [SP]" 344 | msgstr "Baie de Baruch [SP]" 345 | 346 | #: gw2rpc/gw2rpc.py:55 347 | msgid "Riverside [DE]" 348 | msgstr "Provinces fluviales [DE]" 349 | 350 | #: gw2rpc/gw2rpc.py:92 351 | msgid "About" 352 | msgstr "À propos" 353 | 354 | #: gw2rpc/gw2rpc.py:94 355 | msgid "Join support server" 356 | msgstr "Rejoindre le serveur d'assistance" 357 | 358 | #: gw2rpc/gw2rpc.py:97 359 | msgid "Guild Wars 2 with Discord" 360 | msgstr "Guild Wars 2 avec Discord" 361 | 362 | #: gw2rpc/gw2rpc.py:137 363 | msgid "Could not check for updates - check your connection!" 364 | msgstr "Impossible de vérifier les mises à jour - vérifiez votre connexion!" 365 | 366 | #: gw2rpc/gw2rpc.py:143 367 | msgid "There is a new update for GW2 Rich Presence available. Would you like to be taken to the download page now?" 368 | msgstr "Il y a une mise à jour disponible pour GW2 Rich Presence. Souhaitez-vous vous rendre à la page de téléchargement immédiatement?" 369 | 370 | #: gw2rpc/gw2rpc.py:165 371 | msgid "Fractals of the Mists" 372 | msgstr "Fractales des Brumes" 373 | 374 | #: gw2rpc/gw2rpc.py:170 375 | msgid "fractal" 376 | msgstr "fractale" 377 | 378 | #: gw2rpc/gw2rpc.py:198 gw2rpc/gw2rpc.py:199 379 | msgid "in " 380 | msgstr "dans " 381 | 382 | #: gw2rpc/gw2rpc.py:214 383 | msgid "fighting " 384 | msgstr "en combat contre " 385 | 386 | #: gw2rpc/gw2rpc.py:216 387 | msgid "completing " 388 | msgstr "fin du combat contre " 389 | 390 | #: gw2rpc/gw2rpc.py:285 391 | msgid " near " 392 | msgstr " près de " 393 | 394 | #: gw2rpc/gw2rpc.py:302 395 | msgid "in character selection" 396 | msgstr "dans la sélection de personnage" 397 | 398 | #: gw2rpc/gw2rpc.py:307 399 | msgid "Character Selection" 400 | msgstr "Sélection de personnage" 401 | 402 | 403 | msgid "Mistlock Observatory" 404 | msgstr "Sanctuaire de Gardebrume" 405 | 406 | msgid "Uncategorized" 407 | msgstr "Non classée" 408 | 409 | msgid "Snowblind" 410 | msgstr "Aveugleneige" 411 | 412 | msgid "Swampland" 413 | msgstr "Marais" 414 | 415 | msgid "Urban Battleground" 416 | msgstr "Champ de bataille urbain" 417 | 418 | msgid "Urban Battleground" 419 | msgstr "Champ de bataille urbain" 420 | 421 | msgid "Siren's Reef" 422 | msgstr "Récif de la sirène" 423 | 424 | msgid "Aquatic Ruins" 425 | msgstr "Ruines aquatiques" 426 | 427 | msgid "Cliffside" 428 | msgstr "Flanc de falaise" 429 | 430 | msgid "Underground Facility" 431 | msgstr "Complexe souterrain" 432 | 433 | msgid "Volcanic" 434 | msgstr "Volcanique" 435 | 436 | msgid "Molten Furnace" 437 | msgstr "Fournaise de la Fusion" 438 | 439 | msgid "Aetherblade" 440 | msgstr "Etherlame" 441 | 442 | msgid "Thaumanova Reactor" 443 | msgstr "Réacteur de Thaumanova" 444 | 445 | msgid "Solid Ocean" 446 | msgstr "Océan solide" 447 | 448 | msgid "Molten Boss" 449 | msgstr "Boss de la Fusion" 450 | 451 | msgid "Mai Trin" 452 | msgstr "Mai Trin" 453 | 454 | msgid "Chaos" 455 | msgstr "Chaos" 456 | 457 | msgid "Nightmare" 458 | msgstr "Cauchemars" 459 | 460 | msgid "Shattered Observatory" 461 | msgstr "Observatoire détruit" 462 | 463 | msgid "Twilight Oasis" 464 | msgstr "Oasis du crépuscule" 465 | 466 | msgid "Deepstone" 467 | msgstr "Roche des abysses" 468 | 469 | msgid "Sunqua Peak" 470 | msgstr "Pic de Sunqua" 471 | 472 | msgid "Skorvald" 473 | msgstr "Skorvald" 474 | 475 | msgid "Ensolyss" 476 | msgstr "Ensolyss" 477 | 478 | msgid "Ai" 479 | msgstr "Ai" 480 | 481 | msgid "Artsariiv" 482 | msgstr "Artsariiv" 483 | 484 | msgid "Arkk" 485 | msgstr "Arkk" 486 | 487 | msgid "M.A.M.A." 488 | msgstr "A.M.A.M." 489 | 490 | msgid "Siax" 491 | msgstr "Siax" 492 | 493 | msgid "Vale Guardian" 494 | msgstr "Gardien de la Vallée" 495 | 496 | msgid "Spirit Woods" 497 | msgstr "Les Bois des esprits" 498 | 499 | msgid "Gorseval" 500 | msgstr "" 501 | 502 | msgid "Sabetha" 503 | msgstr "" 504 | 505 | msgid "Slothasor" 506 | msgstr "Paressor" 507 | 508 | msgid "Bandit Trio" 509 | msgstr "" 510 | 511 | msgid "Matthias" 512 | msgstr "" 513 | 514 | msgid "Escort" 515 | msgstr "Assaut de la forteresse" 516 | 517 | msgid "Keep Construct" 518 | msgstr "Titan du fort" 519 | 520 | msgid "Xera" 521 | msgstr "" 522 | 523 | msgid "Twisted Castle" 524 | msgstr "Le Château corrompu" 525 | 526 | msgid "Cairn" 527 | msgstr "" 528 | 529 | msgid "Mursaat Overseer" 530 | msgstr "Surveillant mursaat" 531 | 532 | msgid "Samarog" 533 | msgstr "" 534 | 535 | msgid "Deimos" 536 | msgstr "" 537 | 538 | msgid "Soulless Horror" 539 | msgstr "Horreur sans âme" 540 | 541 | msgid "River of Souls" 542 | msgstr "La rivière des âmes" 543 | 544 | msgid "Broken King" 545 | msgstr "Roi brisé" 546 | 547 | msgid "Eater of Souls" 548 | msgstr "Mangeur d'âmes" 549 | 550 | msgid "Dhuum" 551 | msgstr "" 552 | 553 | msgid "Statue of Darkness" 554 | msgstr "Statue des ténèbres" 555 | 556 | msgid "Conjured Amalgamate" 557 | msgstr "Amalgame conjuré" 558 | 559 | msgid "Twin Largos" 560 | msgstr "Jumeaux largos" 561 | 562 | msgid "Qadim" 563 | msgstr "" 564 | 565 | msgid "Cardinal Sabir" 566 | msgstr "" 567 | 568 | msgid "Cardinal Adina" 569 | msgstr "" 570 | 571 | msgid "Qadim the Peerless" 572 | msgstr "Qadim l'Inégalé" 573 | 574 | msgid "Harbinger" 575 | msgstr "Augure" 576 | 577 | msgid "Willbender" 578 | msgstr "Subjugueur" 579 | 580 | msgid "Virtuoso" 581 | msgstr "Virtuose" 582 | 583 | msgid "Catalyst" 584 | msgstr "Catalyseur" 585 | 586 | msgid "Bladesworn" 587 | msgstr "Jurelame" 588 | 589 | msgid "Vindicator" 590 | msgstr "Justicier" 591 | 592 | msgid "Mechanist" 593 | msgstr "Méchamancien" 594 | 595 | msgid "Specter" 596 | msgstr "Spectre" 597 | 598 | msgid "Untamed" 599 | msgstr "Indomptable" 600 | 601 | msgid "Commander" 602 | msgstr "Commandant" 603 | 604 | msgid "jackal" 605 | msgstr "chacal" 606 | 607 | msgid "griffon" 608 | msgstr "griffon" 609 | 610 | msgid "springer" 611 | msgstr "frappesol" 612 | 613 | msgid "skimmer" 614 | msgstr "voldécume" 615 | 616 | msgid "raptor" 617 | msgstr "raptor" 618 | 619 | msgid "roller beetle" 620 | msgstr "scaraboule" 621 | 622 | msgid "warclaw" 623 | msgstr "razziafelis" 624 | 625 | msgid "skyscale" 626 | msgstr "dracaille" 627 | 628 | msgid "siege turtle" 629 | msgstr "tortue de siège" 630 | 631 | msgid "on" 632 | msgstr "à" 633 | 634 | msgid "Whisper of Jormag" 635 | msgstr "Murmure de Jormag" 636 | 637 | msgid "Boneskinner" 638 | msgstr "Désosseur" 639 | 640 | msgid "Fraenir of Jormag" 641 | msgstr "Fraenir de Jormag" 642 | 643 | msgid "Voice And Claw of the Fallen" 644 | msgstr "La Voix et la Griffe des déchus" 645 | 646 | msgid "Legendary Icebrood Construct" 647 | msgstr "Sentinelle couvegivre légendaire" 648 | 649 | msgid "Mistlock Sanctuary" 650 | msgstr "Sanctuaire de Gardebrume" 651 | 652 | msgid "Closest" 653 | msgstr "" 654 | 655 | # Molten boss fractal 656 | msgid "Berserker and Firestorm" 657 | msgstr "Berserker et Tempête de feu" 658 | 659 | # Sirens reef fractal 660 | msgid "Blasting Black Peter" 661 | msgstr "Pete l'explosif" 662 | 663 | # Sirens reef fractal 664 | msgid "Arabella Crowe" 665 | msgstr "Arabella Crowe" 666 | 667 | msgid "Brood Queen" 668 | msgstr "Reina de la colonia" 669 | 670 | msgid "Deepstone Sentinel" 671 | msgstr "Centinela de Rocahonda." 672 | 673 | msgid "The Voice" 674 | msgstr "La Voz" 675 | 676 | msgid "Bloomhunger" 677 | msgstr "Carnasse" 678 | 679 | msgid "Jade Maw" 680 | msgstr "Abîme de Jade" 681 | 682 | msgid "loading screen" 683 | msgstr "écran de chargement" 684 | 685 | msgid "GW2RPC Raid Announcer" 686 | msgstr "" 687 | 688 | msgid "tagged up" 689 | msgstr "" 690 | 691 | msgid "Copy and paste the following to join" 692 | msgstr "" 693 | 694 | msgid "Yes" 695 | msgstr "" 696 | 697 | msgid "No" 698 | msgstr "" 699 | 700 | msgid "Announce raids:" 701 | msgstr "" 702 | 703 | msgid "Closest PoI" 704 | msgstr "" 705 | 706 | msgid "skiff" 707 | msgstr "" 708 | 709 | msgid "Jade Bot" 710 | msgstr "drone de jade" 711 | 712 | msgid "Elemental Source" 713 | msgstr "élémentaire de glace" 714 | 715 | msgid "Lornar Dragonseeker" 716 | msgstr "Lornarr Cherchedragon" 717 | 718 | msgid "Frizz" 719 | msgstr "Frizz" 720 | 721 | msgid "Captain Mai Trin" 722 | msgstr "Capitaine Mai Trin" 723 | 724 | msgid "Anomaly" 725 | msgstr "Anomalie" 726 | 727 | msgid "Brazen Gladiator" 728 | msgstr "Gladiateur audacieux" 729 | 730 | msgid "Amala" 731 | msgstr "Amala" 732 | 733 | msgid "a sandbinder" 734 | msgstr "un ensableur des lanciers du soleil" 735 | 736 | 737 | msgid "Captain Ashym" 738 | msgstr "Capitaine Ashym" 739 | 740 | msgid "Jellyfish Beast" 741 | msgstr "Méduse monstrueuse" 742 | 743 | msgid "Archdiviner" 744 | msgstr "l'archidevin" 745 | 746 | msgid "Rabsovich" 747 | msgstr "" 748 | 749 | msgid "Rampaging Ice Elemental or Dredge Powersuit" 750 | msgstr "draguerre en armure ou élémentaire de glace" 751 | 752 | msgid "Grawl Shaman" 753 | msgstr "le chamane grawl" 754 | 755 | msgid "Imbued Shaman" 756 | msgstr "le chamane transmuté" 757 | 758 | msgid "in Weapon Testing Facility" 759 | msgstr "" 760 | 761 | msgid "Subject 6" 762 | msgstr "Sujet 6" 763 | 764 | msgid "Thaumanova Anomaly" 765 | msgstr "Anomalie de Thaumanova" 766 | 767 | # pre-event of Wing 7 768 | msgid "gate" 769 | msgstr "" 770 | 771 | msgid "Minister Li" 772 | msgstr "" 773 | 774 | msgid "Old Tom" 775 | msgstr "Gros Tom" 776 | 777 | msgid "Raving Asura" 778 | msgstr "forcené asura" 779 | 780 | msgid "Voice of the Mountain" 781 | msgstr "Voix de la montagne" 782 | 783 | msgid "Sorrow of the Mountain" 784 | msgstr "Lamentation de la montagne" 785 | 786 | msgid "Fury of the Mountain" 787 | msgstr "Fureur de la montagne" 788 | 789 | msgid "Dragon Void" 790 | msgstr "Vide du dragon" 791 | 792 | msgid "Silent Surf" 793 | msgstr "Ressac silencieux" -------------------------------------------------------------------------------- /locales/pt-Br/LC_MESSAGES/base.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) YEAR ORGANIZATION 2 | # FIRST AUTHOR , YEAR. 3 | # 4 | msgid "" 5 | msgstr "" 6 | "Project-Id-Version: PACKAGE VERSION\n" 7 | "POT-Creation-Date: 2021-05-28 15:15+0200\n" 8 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 9 | "Last-Translator: FULL NAME \n" 10 | "Language-Team: LANGUAGE \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: 8bit\n" 14 | "Generated-By: pygettext.py 1.5\n" 15 | 16 | 17 | #: gw2rpc/character.py:6 18 | msgid "Guardian" 19 | msgstr "Guardian" 20 | 21 | #: gw2rpc/character.py:7 22 | msgid "Warrior" 23 | msgstr "Warrior" 24 | 25 | #: gw2rpc/character.py:8 26 | msgid "Engineer" 27 | msgstr "Engineer" 28 | 29 | #: gw2rpc/character.py:9 30 | msgid "Ranger" 31 | msgstr "Ranger" 32 | 33 | #: gw2rpc/character.py:10 34 | msgid "Thief" 35 | msgstr "Thief" 36 | 37 | #: gw2rpc/character.py:11 38 | msgid "Elementalist" 39 | msgstr "Elementalist" 40 | 41 | #: gw2rpc/character.py:12 42 | msgid "Mesmer" 43 | msgstr "Mesmer" 44 | 45 | #: gw2rpc/character.py:13 46 | msgid "Necromancer" 47 | msgstr "Necromante" 48 | 49 | #: gw2rpc/character.py:14 50 | msgid "Revenant" 51 | msgstr "Revenant" 52 | 53 | #: gw2rpc/character.py:17 54 | msgid "Asura" 55 | msgstr "Asura" 56 | 57 | #: gw2rpc/character.py:17 58 | msgid "Charr" 59 | msgstr "Charr" 60 | 61 | #: gw2rpc/character.py:17 62 | msgid "Human" 63 | msgstr "Humano" 64 | 65 | #: gw2rpc/character.py:17 66 | msgid "Norn" 67 | msgstr "Norn" 68 | 69 | #: gw2rpc/character.py:17 70 | msgid "Sylvari" 71 | msgstr "Sylvari" 72 | 73 | #: gw2rpc/character.py:20 74 | msgid "Druid" 75 | msgstr "Druid" 76 | 77 | #: gw2rpc/character.py:21 78 | msgid "Daredevil" 79 | msgstr "Daredevil" 80 | 81 | #: gw2rpc/character.py:22 82 | msgid "Berserker" 83 | msgstr "Berserker" 84 | 85 | #: gw2rpc/character.py:23 86 | msgid "Dragonhunter" 87 | msgstr "Dragonhunter" 88 | 89 | #: gw2rpc/character.py:24 90 | msgid "Reaper" 91 | msgstr "Reaper" 92 | 93 | #: gw2rpc/character.py:25 94 | msgid "Chronomancer" 95 | msgstr "Chronomancer" 96 | 97 | #: gw2rpc/character.py:26 98 | msgid "Scrapper" 99 | msgstr "Scrapper" 100 | 101 | #: gw2rpc/character.py:27 102 | msgid "Tempest" 103 | msgstr "Tempest" 104 | 105 | #: gw2rpc/character.py:28 106 | msgid "Herald" 107 | msgstr "Herald" 108 | 109 | #: gw2rpc/character.py:29 110 | msgid "Soulbeast" 111 | msgstr "Soulbeast" 112 | 113 | #: gw2rpc/character.py:30 114 | msgid "Weaver" 115 | msgstr "Weaver" 116 | 117 | #: gw2rpc/character.py:31 118 | msgid "Holosmith" 119 | msgstr "Holosmith" 120 | 121 | #: gw2rpc/character.py:32 122 | msgid "Deadeye" 123 | msgstr "Deadeye" 124 | 125 | #: gw2rpc/character.py:33 126 | msgid "Mirage" 127 | msgstr "Mirage" 128 | 129 | #: gw2rpc/character.py:34 130 | msgid "Scourge" 131 | msgstr "Scourge" 132 | 133 | #: gw2rpc/character.py:35 134 | msgid "Spellbreaker" 135 | msgstr "Spellbreaker" 136 | 137 | #: gw2rpc/character.py:36 138 | msgid "Firebrand" 139 | msgstr "Firebrand" 140 | 141 | #: gw2rpc/character.py:37 142 | msgid "Renegade" 143 | msgstr "Renegade" 144 | 145 | #: gw2rpc/gw2rpc.py:39 146 | msgid "Anvil Rock" 147 | msgstr "Bigorna de Pedra" 148 | 149 | #: gw2rpc/gw2rpc.py:39 150 | msgid "Blackgate" 151 | msgstr "Portão Negro" 152 | 153 | #: gw2rpc/gw2rpc.py:39 154 | msgid "Borlis Pass" 155 | msgstr "Passagem de Borlis" 156 | 157 | #: gw2rpc/gw2rpc.py:39 158 | msgid "Crystal Desert" 159 | msgstr "Deserto do Cristal" 160 | 161 | #: gw2rpc/gw2rpc.py:40 162 | msgid "Darkhaven" 163 | msgstr "Porto Escuro" 164 | 165 | #: gw2rpc/gw2rpc.py:40 166 | msgid "Devona's Rest" 167 | msgstr "Descanso de Devona" 168 | 169 | #: gw2rpc/gw2rpc.py:40 170 | msgid "Dragonbrand" 171 | msgstr "Marca do Dragão" 172 | 173 | #: gw2rpc/gw2rpc.py:40 174 | msgid "Ehmry Bay" 175 | msgstr "Baía de Ehmry" 176 | 177 | #: gw2rpc/gw2rpc.py:41 178 | msgid "Eredon Terrace" 179 | msgstr "Terraço de Eredon" 180 | 181 | #: gw2rpc/gw2rpc.py:41 182 | msgid "Ferguson's Crossing" 183 | msgstr "Travessia de Ferguson" 184 | 185 | #: gw2rpc/gw2rpc.py:41 186 | msgid "Fort Aspenwood" 187 | msgstr "Forte de Aspenwood" 188 | 189 | #: gw2rpc/gw2rpc.py:42 190 | msgid "Gate of Madness" 191 | msgstr "Portão da Loucura" 192 | 193 | #: gw2rpc/gw2rpc.py:42 194 | msgid "Henge of Denravi" 195 | msgstr "Henge de Denravi" 196 | 197 | #: gw2rpc/gw2rpc.py:42 198 | msgid "Isle of Janthir" 199 | msgstr "Ilha de Janthir" 200 | 201 | #: gw2rpc/gw2rpc.py:43 202 | msgid "Jade Quarry" 203 | msgstr "Pedreira de Jade" 204 | 205 | #: gw2rpc/gw2rpc.py:43 206 | msgid "Kaineng" 207 | msgstr "Cidade de Kaineng" 208 | 209 | #: gw2rpc/gw2rpc.py:43 210 | msgid "Maguuma" 211 | msgstr "Resíduos de Magumma" 212 | 213 | #: gw2rpc/gw2rpc.py:43 214 | msgid "Northern Shiverpeaks" 215 | msgstr "Norte de Shiverpeaks" 216 | 217 | #: gw2rpc/gw2rpc.py:44 218 | msgid "Sanctum of Rall" 219 | msgstr "Santuário de Rall" 220 | 221 | #: gw2rpc/gw2rpc.py:44 222 | msgid "Sea of Sorrows" 223 | msgstr "Mar do Infotunio" 224 | 225 | #: gw2rpc/gw2rpc.py:44 226 | msgid "Sorrow's Furnace" 227 | msgstr "Forno de Sorrow" 228 | 229 | #: gw2rpc/gw2rpc.py:45 230 | msgid "Stormbluff Isle" 231 | msgstr "Ilha de Stormbluff" 232 | 233 | #: gw2rpc/gw2rpc.py:45 234 | msgid "Tarnished Coast" 235 | msgstr "Costa de Tarnished" 236 | 237 | #: gw2rpc/gw2rpc.py:45 238 | msgid "Yak's Bend" 239 | msgstr "Curva dos Yaks" 240 | 241 | #: gw2rpc/gw2rpc.py:48 242 | msgid "Aurora Glade" 243 | msgstr "Clareiras de Aurora" 244 | 245 | #: gw2rpc/gw2rpc.py:48 246 | msgid "Blacktide" 247 | msgstr "Blacktide" 248 | 249 | #: gw2rpc/gw2rpc.py:48 250 | msgid "Desolation" 251 | msgstr "Desolação" 252 | 253 | #: gw2rpc/gw2rpc.py:48 254 | msgid "Far Shiverpeaks" 255 | msgstr "Encosta de Shiverpeaks" 256 | 257 | #: gw2rpc/gw2rpc.py:49 258 | msgid "Fissure of Woe" 259 | msgstr "Fissura de Woe" 260 | 261 | #: gw2rpc/gw2rpc.py:49 262 | msgid "Gandara" 263 | msgstr "Gandara" 264 | 265 | #: gw2rpc/gw2rpc.py:49 266 | msgid "Gunnar's Hold" 267 | msgstr "Domínio de Gunnar" 268 | 269 | #: gw2rpc/gw2rpc.py:49 270 | msgid "Piken Square" 271 | msgstr "Praça de Piken" 272 | 273 | #: gw2rpc/gw2rpc.py:50 274 | msgid "Ring of Fire" 275 | msgstr "Anel de fogo" 276 | 277 | #: gw2rpc/gw2rpc.py:50 278 | msgid "Ruins of Surmia" 279 | msgstr "Ruínas de Surmia" 280 | 281 | #: gw2rpc/gw2rpc.py:50 282 | msgid "Seafarer's Rest" 283 | msgstr "Pousada de Seafarer" 284 | 285 | #: gw2rpc/gw2rpc.py:50 286 | msgid "Underworld" 287 | msgstr "Submundo" 288 | 289 | #: gw2rpc/gw2rpc.py:51 290 | msgid "Arborstone [FR]" 291 | msgstr "Pedra Angular [FR]" 292 | 293 | #: gw2rpc/gw2rpc.py:51 294 | msgid "Augury Rock [FR]" 295 | msgstr "Rocha de Augúrio [FR]" 296 | 297 | #: gw2rpc/gw2rpc.py:51 298 | msgid "Vabbi" 299 | msgstr "Vabbi" 300 | 301 | #: gw2rpc/gw2rpc.py:51 302 | msgid "Whiteside Ridge" 303 | msgstr "Aresta do Whiteside" 304 | 305 | #: gw2rpc/gw2rpc.py:52 306 | msgid "Fort Ranik [FR]" 307 | msgstr "Forte Ranik [FR]" 308 | 309 | #: gw2rpc/gw2rpc.py:52 310 | msgid "Jade Sea [FR]" 311 | msgstr "Mar de Jade [FR]" 312 | 313 | #: gw2rpc/gw2rpc.py:52 314 | msgid "Vizunah Square [FR]" 315 | msgstr "Praça de Vizunah [FR]" 316 | 317 | #: gw2rpc/gw2rpc.py:53 318 | msgid "Abaddon's Mouth [DE]" 319 | msgstr "Boca de Abaddon [DE]" 320 | 321 | #: gw2rpc/gw2rpc.py:53 322 | msgid "Drakkar Lake [DE]" 323 | msgstr "Lago Drakkar [DE]" 324 | 325 | #: gw2rpc/gw2rpc.py:53 326 | msgid "Dzagonur [DE]" 327 | msgstr "Dzagonur [DE]" 328 | 329 | #: gw2rpc/gw2rpc.py:54 330 | msgid "Elona Reach [DE]" 331 | msgstr "Extensão de Elona [DE]" 332 | 333 | #: gw2rpc/gw2rpc.py:54 334 | msgid "Kodash [DE]" 335 | msgstr "Kodash [DE]" 336 | 337 | #: gw2rpc/gw2rpc.py:54 338 | msgid "Miller's Sound [DE]" 339 | msgstr "Sons de Miller [DE]" 340 | 341 | #: gw2rpc/gw2rpc.py:55 342 | msgid "Baruch Bay [SP]" 343 | msgstr "Baía de Baruch [SP]" 344 | 345 | #: gw2rpc/gw2rpc.py:55 346 | msgid "Riverside [DE]" 347 | msgstr "Riverside [DE]" 348 | 349 | #: gw2rpc/gw2rpc.py:92 350 | msgid "About" 351 | msgstr "Sobre" 352 | 353 | #: gw2rpc/gw2rpc.py:94 354 | msgid "Join support server" 355 | msgstr "Entre no Servidor de Suporte" 356 | 357 | #: gw2rpc/gw2rpc.py:97 358 | msgid "Guild Wars 2 with Discord" 359 | msgstr "Guild wars 2 com Discord" 360 | 361 | #: gw2rpc/gw2rpc.py:137 362 | msgid "Could not check for updates - check your connection!" 363 | msgstr "Não foi possível verificar actualizações - verifique a sua conexão!" 364 | 365 | #: gw2rpc/gw2rpc.py:143 366 | msgid "There is a new update for GW2 Rich Presence available. Would you like to be taken to the download page now?" 367 | msgstr "Está disponível uma nova atualização para GW2 Rich Presence. Gostaria de ser levado para a página de download agora?" 368 | 369 | #: gw2rpc/gw2rpc.py:165 370 | msgid "Fractals of the Mists" 371 | msgstr "Fractais das Névoas" 372 | 373 | #: gw2rpc/gw2rpc.py:170 374 | msgid " fractal" 375 | msgstr " fractais" 376 | 377 | #: gw2rpc/gw2rpc.py:198 gw2rpc/gw2rpc.py:199 378 | msgid "in " 379 | msgstr "em " 380 | 381 | #: gw2rpc/gw2rpc.py:214 382 | msgid "fighting " 383 | msgstr "Lutando " 384 | 385 | #: gw2rpc/gw2rpc.py:216 386 | msgid "completing " 387 | msgstr "Completando " 388 | 389 | #: gw2rpc/gw2rpc.py:285 390 | msgid " near " 391 | msgstr " próximo(a) a " 392 | 393 | #: gw2rpc/gw2rpc.py:302 394 | msgid "in character selection" 395 | msgstr "Na seleção de Personagens" 396 | 397 | #: gw2rpc/gw2rpc.py:307 398 | msgid "Character Selection" 399 | msgstr "Seleção de personagem" 400 | 401 | 402 | msgid "Mistlock Observatory" 403 | msgstr "Observatório Mistlock" 404 | 405 | msgid "Uncategorized" 406 | msgstr "Sem Categoria" 407 | 408 | msgid "Snowblind" 409 | msgstr "Snowblind" 410 | 411 | msgid "Swampland" 412 | msgstr "Pântano" 413 | 414 | msgid "Urban Battleground" 415 | msgstr "Campo de batalha urbano" 416 | 417 | msgid "Siren's Reef" 418 | msgstr "Recife da Sereia" 419 | 420 | msgid "Aquatic Ruins" 421 | msgstr "Ruínas Aquáticas" 422 | 423 | msgid "Cliffside" 424 | msgstr "Penhasco" 425 | 426 | msgid "Underground Facility" 427 | msgstr "Instalações Subterrâneas" 428 | 429 | msgid "Volcanic" 430 | msgstr "Vulcânico" 431 | 432 | msgid "Molten Furnace" 433 | msgstr "Forno Fundido" 434 | 435 | msgid "Aetherblade" 436 | msgstr "Lâmina do Éter" 437 | 438 | msgid "Thaumanova Reactor" 439 | msgstr "Reator Thaumanova" 440 | 441 | msgid "Solid Ocean" 442 | msgstr "Oceano Sólido" 443 | 444 | msgid "Molten Boss" 445 | msgstr "Chefão Derretido" 446 | 447 | msgid "Mai Trin" 448 | msgstr "Mai Trin" 449 | 450 | msgid "Chaos" 451 | msgstr "Caos" 452 | 453 | msgid "Nightmare" 454 | msgstr "Pesadelo" 455 | 456 | msgid "Shattered Observatory" 457 | msgstr "Observatório Despedaçado" 458 | 459 | msgid "Twilight Oasis" 460 | msgstr "Oásis do Crepúsculo" 461 | 462 | msgid "Deepstone" 463 | msgstr "Deepstone" 464 | 465 | msgid "Sunqua Peak" 466 | msgstr "Pico Sunqua" 467 | 468 | msgid "Skorvald" 469 | msgstr "Skorvald" 470 | 471 | msgid "Ensolyss" 472 | msgstr "Ensolyss" 473 | 474 | msgid "Ai" 475 | msgstr "Ai" 476 | 477 | msgid "Artsariiv" 478 | msgstr "Artsariiv" 479 | 480 | msgid "Arkk" 481 | msgstr "Arkk" 482 | 483 | msgid "M.A.M.A." 484 | msgstr "M.A.M.A" 485 | 486 | msgid "Siax" 487 | msgstr "Siax" 488 | 489 | msgid "Vale Guardian" 490 | msgstr "Vale do Guardião" 491 | 492 | msgid "Spirit Woods" 493 | msgstr "Bosque Espíritual" 494 | 495 | msgid "Gorseval" 496 | msgstr "Gorseval" 497 | 498 | msgid "Sabetha" 499 | msgstr "Sabetha" 500 | 501 | msgid "Slothasor" 502 | msgstr "Slothasor" 503 | 504 | msgid "Bandit Trio" 505 | msgstr "Bandit Trio" 506 | 507 | msgid "Matthias" 508 | msgstr "Matthias" 509 | 510 | msgid "Escort" 511 | msgstr "Escolta" 512 | 513 | msgid "Keep Construct" 514 | msgstr "Construção Conservada" 515 | 516 | msgid "Xera" 517 | msgstr "Xera" 518 | 519 | msgid "Twisted Castle" 520 | msgstr "Castelo Retorcido" 521 | 522 | msgid "Cairn" 523 | msgstr "Cairn" 524 | 525 | msgid "Mursaat Overseer" 526 | msgstr "Supervisor de Mursaat" 527 | 528 | msgid "Samarog" 529 | msgstr "Samarog" 530 | 531 | msgid "Deimos" 532 | msgstr "Deimos" 533 | 534 | msgid "Soulless Horror" 535 | msgstr "Repúdio sem Alma" 536 | 537 | msgid "River of Souls" 538 | msgstr "Rio das Almas" 539 | 540 | msgid "Broken King" 541 | msgstr "Rei Partido" 542 | 543 | msgid "Eater of Souls" 544 | msgstr "Devorador de Almas" 545 | 546 | msgid "Dhuum" 547 | msgstr "Dhuum" 548 | 549 | msgid "Statue of Darkness" 550 | msgstr "Estátua das Trevas" 551 | 552 | msgid "Conjured Amalgamate" 553 | msgstr "Amálgama Conjugada" 554 | 555 | msgid "Twin Largos" 556 | msgstr "Gémeos Largos" 557 | 558 | msgid "Qadim" 559 | msgstr "Qadim" 560 | 561 | msgid "Cardinal Sabir" 562 | msgstr "Cardeal Sabir" 563 | 564 | msgid "Cardinal Adina" 565 | msgstr "Cardeal Adina" 566 | 567 | msgid "Qadim the Peerless" 568 | msgstr "Qadim sem Par" 569 | 570 | msgid "Harbinger" 571 | msgstr "Harbinger" 572 | 573 | msgid "Willbender" 574 | msgstr "Willbender" 575 | 576 | msgid "Virtuoso" 577 | msgstr "Virtuoso" 578 | 579 | msgid "Catalyst" 580 | msgstr "Catalizador" 581 | 582 | msgid "Bladesworn" 583 | msgstr "Desgaste da Lâmina" 584 | 585 | msgid "Vindicator" 586 | msgstr "Vindicador" 587 | 588 | msgid "Mechanist" 589 | msgstr "Mecânico" 590 | 591 | msgid "Specter" 592 | msgstr "Espectro" 593 | 594 | msgid "Untamed" 595 | msgstr "Untamed" 596 | 597 | msgid "Commander" 598 | msgstr "Comandante" 599 | 600 | msgid "jackal" 601 | msgstr "Jackal" 602 | 603 | msgid "griffon" 604 | msgstr "Grifo" 605 | 606 | msgid "springer" 607 | msgstr "Springer" 608 | 609 | msgid "skimmer" 610 | msgstr "Skimmer" 611 | 612 | msgid "raptor" 613 | msgstr "Raptor" 614 | 615 | msgid "roller beetle" 616 | msgstr "Roller beetle" 617 | 618 | msgid "warclaw" 619 | msgstr "Warclaw" 620 | 621 | msgid "skyscale" 622 | msgstr "Skyscale" 623 | 624 | msgid "siege turtle" 625 | msgstr "Taruga" 626 | 627 | msgid "on" 628 | msgstr "em" 629 | 630 | msgid "Whisper of Jormag" 631 | msgstr "Sussuros do Jormag" 632 | 633 | msgid "Boneskinner" 634 | msgstr "Pele de Ossos" 635 | 636 | msgid "Fraenir of Jormag" 637 | msgstr "Fraenir de Jormag" 638 | 639 | msgid "Voice And Claw of the Fallen" 640 | msgstr "A Voz e a Garra dos Caídos" 641 | 642 | msgid "Legendary Icebrood Construct" 643 | msgstr "Construção lendária de Icebrood" 644 | 645 | msgid "Mistlock Sanctuary" 646 | msgstr "Santuário de Mislock" 647 | 648 | msgid "Closest" 649 | msgstr "Próximo" 650 | 651 | # Molten boss fractal 652 | msgid "Berserker and Firestorm" 653 | msgstr "Berserker e Firestorm" 654 | 655 | # Sirens reef fractal 656 | msgid "Blasting Black Peter" 657 | msgstr "Jateamento do Black Peter" 658 | 659 | # Sirens reef fractal 660 | msgid "Arabella Crowe" 661 | msgstr "Arabella Crowe" 662 | 663 | msgid "Brood Queen" 664 | msgstr "Rainha da Criação" 665 | 666 | msgid "Deepstone Sentinel" 667 | msgstr "Sentinela de pedra profunda" 668 | 669 | msgid "The Voice" 670 | msgstr "A Voz" 671 | 672 | msgid "Bloomhunger" 673 | msgstr "Florescimento da fome" 674 | 675 | msgid "Jade Maw" 676 | msgstr "Jade Maw" 677 | 678 | msgid "loading screen" 679 | msgstr "Tela de Carregamento..." 680 | 681 | msgid "GW2RPC Raid Announcer" 682 | msgstr "GW2RPC - Anúncio de Raid" 683 | 684 | msgid "tagged up" 685 | msgstr "Marcado" 686 | 687 | msgid "Copy and paste the following to join" 688 | msgstr "Copiar e colar para seguidor entrar" 689 | 690 | msgid "Yes" 691 | msgstr "Sim" 692 | 693 | msgid "No" 694 | msgstr "Não" 695 | 696 | msgid "Announce raids:" 697 | msgstr "Anúncio de Raid:" 698 | 699 | msgid "Closest PoI" 700 | msgstr "PoI mais Próximo" 701 | 702 | msgid "skiff" 703 | msgstr "skiff" 704 | 705 | msgid "Jade Bot" 706 | msgstr "Jade Bot" 707 | 708 | msgid "Elemental Source" 709 | msgstr "Fonte Elemental" 710 | 711 | msgid "Lornar Dragonseeker" 712 | msgstr "Lornar Dragonseeker" 713 | 714 | msgid "Frizz" 715 | msgstr "Frizz" 716 | 717 | msgid "Captain Mai Trin" 718 | msgstr "Capitã Mai Trin" 719 | 720 | msgid "Anomaly" 721 | msgstr "Anomalia" 722 | 723 | msgid "Brazen Gladiator" 724 | msgstr "Gladiador " 725 | 726 | msgid "Amala" 727 | msgstr "Amala" 728 | 729 | msgid "a sandbinder" 730 | msgstr "um encadernador" 731 | 732 | msgid "Captain Mai Trin" 733 | msgstr "Capitã Mai Trin" 734 | 735 | msgid "Anomaly" 736 | msgstr "Anomalia" 737 | 738 | msgid "Brazen Gladiator" 739 | msgstr "Gladiador " 740 | 741 | msgid "Amala" 742 | msgstr "Amala" 743 | 744 | msgid "a sandbinder" 745 | msgstr "um encadernador" 746 | 747 | msgid "Captain Ashym" 748 | msgstr "Capitão Ashym" 749 | 750 | msgid "Jellyfish Beast" 751 | msgstr "Água-viva Monstruosa" 752 | 753 | msgid "Archdiviner" 754 | msgstr "Arquidivino" 755 | 756 | msgid "Rabsovich" 757 | msgstr "Rabsovich" 758 | 759 | msgid "Rampaging Ice Elemental or Dredge Powersuit" 760 | msgstr "Elemental de Gelo Agressivo ou " 761 | 762 | msgid "Grawl Shaman" 763 | msgstr "Xamã Grawl" 764 | 765 | msgid "Imbued Shaman" 766 | msgstr "Xamã Imbuído" 767 | 768 | msgid "in Weapon Testing Facility" 769 | msgstr "na instalação de teste de armas" 770 | 771 | msgid "Subject 6" 772 | msgstr "Sujeito 6" 773 | 774 | msgid "Thaumanova Anomaly" 775 | msgstr "Anomalia Thaumanova" 776 | 777 | # pre-event of Wing 7 778 | msgid "gate" 779 | msgstr "portão" 780 | 781 | msgid "Minister Li" 782 | msgstr "Ministro Li" 783 | 784 | msgid "Old Tom" 785 | msgstr "Velho Tom" 786 | 787 | msgid "Raving Asura" 788 | msgstr "Asura Delirante" 789 | 790 | msgid "Voice of the Mountain" 791 | msgstr "Voz da Montanha" 792 | 793 | msgid "Sorrow of the Mountain" 794 | msgstr "Montanha da Tristeza" 795 | 796 | msgid "Fury of the Mountain" 797 | msgstr "Fúria da Montanha" 798 | 799 | msgid "Dragon Void" 800 | msgstr "Vazio do Dragão" 801 | 802 | msgid "Silent Surf" 803 | msgstr "" -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | infi.systray 3 | psutil 4 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import logging.handlers 3 | 4 | from gw2rpc.gw2rpc import GW2RPC 5 | from gw2rpc.settings import config 6 | 7 | def setup_logging(): 8 | formatter = logging.Formatter( 9 | '%(asctime)s:%(levelname)s:%(name)s: %(message)s') 10 | handler = logging.handlers.RotatingFileHandler( 11 | filename="gw2rpc.log", maxBytes=5 * 1024 * 1024, encoding='utf-8') 12 | handler.setFormatter(formatter) 13 | # stderr_hdlr = logging.StreamHandler() 14 | logger = logging.getLogger("") 15 | logger.setLevel(config.log_level) 16 | # logger.addHandler(stderr_hdlr) 17 | logger.addHandler(handler) 18 | 19 | 20 | if __name__ == "__main__": 21 | setup_logging() 22 | rpc = GW2RPC() 23 | rpc.main_loop() 24 | --------------------------------------------------------------------------------