├── .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 | 
2 |
3 | Guild Wars 2 RPC
4 | A Discord Rich Presence addon for Guild Wars 2.
5 |
6 |
7 |
8 |
9 |
10 |
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 |
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.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 |
--------------------------------------------------------------------------------