├── .github
└── FUNDING.yml
├── LICENSE.md
├── PresenceClient
├── .editorconfig
├── .gitignore
├── PresenceClient-CLI
│ ├── ConsoleOptions.cs
│ ├── Icon.ico
│ ├── PresenceClient-CLI.csproj
│ └── Program.cs
├── PresenceClient-GUI
│ ├── .editorconfig
│ ├── App.config
│ ├── Config.cs
│ ├── MainForm.Designer.cs
│ ├── MainForm.cs
│ ├── MainForm.resx
│ ├── PresenceClient-GUI.csproj
│ ├── Program.cs
│ ├── Properties
│ │ ├── AssemblyInfo.cs
│ │ ├── Resources.Designer.cs
│ │ ├── Resources.resx
│ │ ├── Settings.Designer.cs
│ │ └── Settings.settings
│ ├── Resources
│ │ ├── Connected.ico
│ │ ├── Disconnected.ico
│ │ └── Icon.ico
│ ├── Utils.cs
│ └── packages.config
├── PresenceClient-Py
│ ├── README.md
│ └── presence-client.py
├── PresenceClient.sln
└── PresenceCommon
│ ├── DataHandler.cs
│ ├── PresenceCommon.csproj
│ ├── Types
│ └── Title.cs
│ └── Utils.cs
├── README.md
└── Resource
├── QuestApplicationOverrides.json
└── SwitchApplicationOverrides.json
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | custom: https://link.sunthecourier.net/paypal
2 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 |
2 | GNU GENERAL PUBLIC LICENSE
3 | Version 2, June 1991
4 |
5 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
6 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
7 | Everyone is permitted to copy and distribute verbatim copies
8 | of this license document, but changing it is not allowed.
9 |
10 | Preamble
11 |
12 | The licenses for most software are designed to take away your
13 | freedom to share and change it. By contrast, the GNU General Public
14 | License is intended to guarantee your freedom to share and change free
15 | software--to make sure the software is free for all its users. This
16 | General Public License applies to most of the Free Software
17 | Foundation's software and to any other program whose authors commit to
18 | using it. (Some other Free Software Foundation software is covered by
19 | the GNU Lesser General Public License instead.) You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | this service if you wish), that you receive source code or can get it
26 | if you want it, that you can change the software or use pieces of it
27 | in new free programs; and that you know you can do these things.
28 |
29 | To protect your rights, we need to make restrictions that forbid
30 | anyone to deny you these rights or to ask you to surrender the rights.
31 | These restrictions translate to certain responsibilities for you if you
32 | distribute copies of the software, or if you modify it.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must give the recipients all the rights that
36 | you have. You must make sure that they, too, receive or can get the
37 | source code. And you must show them these terms so they know their
38 | rights.
39 |
40 | We protect your rights with two steps: (1) copyright the software, and
41 | (2) offer you this license which gives you legal permission to copy,
42 | distribute and/or modify the software.
43 |
44 | Also, for each author's protection and ours, we want to make certain
45 | that everyone understands that there is no warranty for this free
46 | software. If the software is modified by someone else and passed on, we
47 | want its recipients to know that what they have is not the original, so
48 | that any problems introduced by others will not reflect on the original
49 | authors' reputations.
50 |
51 | Finally, any free program is threatened constantly by software
52 | patents. We wish to avoid the danger that redistributors of a free
53 | program will individually obtain patent licenses, in effect making the
54 | program proprietary. To prevent this, we have made it clear that any
55 | patent must be licensed for everyone's free use or not licensed at all.
56 |
57 | The precise terms and conditions for copying, distribution and
58 | modification follow.
59 |
60 | GNU GENERAL PUBLIC LICENSE
61 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
62 |
63 | 0. This License applies to any program or other work which contains
64 | a notice placed by the copyright holder saying it may be distributed
65 | under the terms of this General Public License. The "Program", below,
66 | refers to any such program or work, and a "work based on the Program"
67 | means either the Program or any derivative work under copyright law:
68 | that is to say, a work containing the Program or a portion of it,
69 | either verbatim or with modifications and/or translated into another
70 | language. (Hereinafter, translation is included without limitation in
71 | the term "modification".) Each licensee is addressed as "you".
72 |
73 | Activities other than copying, distribution and modification are not
74 | covered by this License; they are outside its scope. The act of
75 | running the Program is not restricted, and the output from the Program
76 | is covered only if its contents constitute a work based on the
77 | Program (independent of having been made by running the Program).
78 | Whether that is true depends on what the Program does.
79 |
80 | 1. You may copy and distribute verbatim copies of the Program's
81 | source code as you receive it, in any medium, provided that you
82 | conspicuously and appropriately publish on each copy an appropriate
83 | copyright notice and disclaimer of warranty; keep intact all the
84 | notices that refer to this License and to the absence of any warranty;
85 | and give any other recipients of the Program a copy of this License
86 | along with the Program.
87 |
88 | You may charge a fee for the physical act of transferring a copy, and
89 | you may at your option offer warranty protection in exchange for a fee.
90 |
91 | 2. You may modify your copy or copies of the Program or any portion
92 | of it, thus forming a work based on the Program, and copy and
93 | distribute such modifications or work under the terms of Section 1
94 | above, provided that you also meet all of these conditions:
95 |
96 | a) You must cause the modified files to carry prominent notices
97 | stating that you changed the files and the date of any change.
98 |
99 | b) You must cause any work that you distribute or publish, that in
100 | whole or in part contains or is derived from the Program or any
101 | part thereof, to be licensed as a whole at no charge to all third
102 | parties under the terms of this License.
103 |
104 | c) If the modified program normally reads commands interactively
105 | when run, you must cause it, when started running for such
106 | interactive use in the most ordinary way, to print or display an
107 | announcement including an appropriate copyright notice and a
108 | notice that there is no warranty (or else, saying that you provide
109 | a warranty) and that users may redistribute the program under
110 | these conditions, and telling the user how to view a copy of this
111 | License. (Exception: if the Program itself is interactive but
112 | does not normally print such an announcement, your work based on
113 | the Program is not required to print an announcement.)
114 |
115 | These requirements apply to the modified work as a whole. If
116 | identifiable sections of that work are not derived from the Program,
117 | and can be reasonably considered independent and separate works in
118 | themselves, then this License, and its terms, do not apply to those
119 | sections when you distribute them as separate works. But when you
120 | distribute the same sections as part of a whole which is a work based
121 | on the Program, the distribution of the whole must be on the terms of
122 | this License, whose permissions for other licensees extend to the
123 | entire whole, and thus to each and every part regardless of who wrote it.
124 |
125 | Thus, it is not the intent of this section to claim rights or contest
126 | your rights to work written entirely by you; rather, the intent is to
127 | exercise the right to control the distribution of derivative or
128 | collective works based on the Program.
129 |
130 | In addition, mere aggregation of another work not based on the Program
131 | with the Program (or with a work based on the Program) on a volume of
132 | a storage or distribution medium does not bring the other work under
133 | the scope of this License.
134 |
135 | 3. You may copy and distribute the Program (or a work based on it,
136 | under Section 2) in object code or executable form under the terms of
137 | Sections 1 and 2 above provided that you also do one of the following:
138 |
139 | a) Accompany it with the complete corresponding machine-readable
140 | source code, which must be distributed under the terms of Sections
141 | 1 and 2 above on a medium customarily used for software interchange; or,
142 |
143 | b) Accompany it with a written offer, valid for at least three
144 | years, to give any third party, for a charge no more than your
145 | cost of physically performing source distribution, a complete
146 | machine-readable copy of the corresponding source code, to be
147 | distributed under the terms of Sections 1 and 2 above on a medium
148 | customarily used for software interchange; or,
149 |
150 | c) Accompany it with the information you received as to the offer
151 | to distribute corresponding source code. (This alternative is
152 | allowed only for noncommercial distribution and only if you
153 | received the program in object code or executable form with such
154 | an offer, in accord with Subsection b above.)
155 |
156 | The source code for a work means the preferred form of the work for
157 | making modifications to it. For an executable work, complete source
158 | code means all the source code for all modules it contains, plus any
159 | associated interface definition files, plus the scripts used to
160 | control compilation and installation of the executable. However, as a
161 | special exception, the source code distributed need not include
162 | anything that is normally distributed (in either source or binary
163 | form) with the major components (compiler, kernel, and so on) of the
164 | operating system on which the executable runs, unless that component
165 | itself accompanies the executable.
166 |
167 | If distribution of executable or object code is made by offering
168 | access to copy from a designated place, then offering equivalent
169 | access to copy the source code from the same place counts as
170 | distribution of the source code, even though third parties are not
171 | compelled to copy the source along with the object code.
172 |
173 | 4. You may not copy, modify, sublicense, or distribute the Program
174 | except as expressly provided under this License. Any attempt
175 | otherwise to copy, modify, sublicense or distribute the Program is
176 | void, and will automatically terminate your rights under this License.
177 | However, parties who have received copies, or rights, from you under
178 | this License will not have their licenses terminated so long as such
179 | parties remain in full compliance.
180 |
181 | 5. You are not required to accept this License, since you have not
182 | signed it. However, nothing else grants you permission to modify or
183 | distribute the Program or its derivative works. These actions are
184 | prohibited by law if you do not accept this License. Therefore, by
185 | modifying or distributing the Program (or any work based on the
186 | Program), you indicate your acceptance of this License to do so, and
187 | all its terms and conditions for copying, distributing or modifying
188 | the Program or works based on it.
189 |
190 | 6. Each time you redistribute the Program (or any work based on the
191 | Program), the recipient automatically receives a license from the
192 | original licensor to copy, distribute or modify the Program subject to
193 | these terms and conditions. You may not impose any further
194 | restrictions on the recipients' exercise of the rights granted herein.
195 | You are not responsible for enforcing compliance by third parties to
196 | this License.
197 |
198 | 7. If, as a consequence of a court judgment or allegation of patent
199 | infringement or for any other reason (not limited to patent issues),
200 | conditions are imposed on you (whether by court order, agreement or
201 | otherwise) that contradict the conditions of this License, they do not
202 | excuse you from the conditions of this License. If you cannot
203 | distribute so as to satisfy simultaneously your obligations under this
204 | License and any other pertinent obligations, then as a consequence you
205 | may not distribute the Program at all. For example, if a patent
206 | license would not permit royalty-free redistribution of the Program by
207 | all those who receive copies directly or indirectly through you, then
208 | the only way you could satisfy both it and this License would be to
209 | refrain entirely from distribution of the Program.
210 |
211 | If any portion of this section is held invalid or unenforceable under
212 | any particular circumstance, the balance of the section is intended to
213 | apply and the section as a whole is intended to apply in other
214 | circumstances.
215 |
216 | It is not the purpose of this section to induce you to infringe any
217 | patents or other property right claims or to contest validity of any
218 | such claims; this section has the sole purpose of protecting the
219 | integrity of the free software distribution system, which is
220 | implemented by public license practices. Many people have made
221 | generous contributions to the wide range of software distributed
222 | through that system in reliance on consistent application of that
223 | system; it is up to the author/donor to decide if he or she is willing
224 | to distribute software through any other system and a licensee cannot
225 | impose that choice.
226 |
227 | This section is intended to make thoroughly clear what is believed to
228 | be a consequence of the rest of this License.
229 |
230 | 8. If the distribution and/or use of the Program is restricted in
231 | certain countries either by patents or by copyrighted interfaces, the
232 | original copyright holder who places the Program under this License
233 | may add an explicit geographical distribution limitation excluding
234 | those countries, so that distribution is permitted only in or among
235 | countries not thus excluded. In such case, this License incorporates
236 | the limitation as if written in the body of this License.
237 |
238 | 9. The Free Software Foundation may publish revised and/or new versions
239 | of the General Public License from time to time. Such new versions will
240 | be similar in spirit to the present version, but may differ in detail to
241 | address new problems or concerns.
242 |
243 | Each version is given a distinguishing version number. If the Program
244 | specifies a version number of this License which applies to it and "any
245 | later version", you have the option of following the terms and conditions
246 | either of that version or of any later version published by the Free
247 | Software Foundation. If the Program does not specify a version number of
248 | this License, you may choose any version ever published by the Free Software
249 | Foundation.
250 |
251 | 10. If you wish to incorporate parts of the Program into other free
252 | programs whose distribution conditions are different, write to the author
253 | to ask for permission. For software which is copyrighted by the Free
254 | Software Foundation, write to the Free Software Foundation; we sometimes
255 | make exceptions for this. Our decision will be guided by the two goals
256 | of preserving the free status of all derivatives of our free software and
257 | of promoting the sharing and reuse of software generally.
258 |
259 | NO WARRANTY
260 |
261 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
262 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
263 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
264 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
265 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
266 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
267 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
268 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
269 | REPAIR OR CORRECTION.
270 |
271 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
272 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
273 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
274 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
275 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
276 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
277 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
278 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
279 | POSSIBILITY OF SUCH DAMAGES.
280 |
281 | END OF TERMS AND CONDITIONS
282 |
283 | How to Apply These Terms to Your New Programs
284 |
285 | If you develop a new program, and you want it to be of the greatest
286 | possible use to the public, the best way to achieve this is to make it
287 | free software which everyone can redistribute and change under these terms.
288 |
289 | To do so, attach the following notices to the program. It is safest
290 | to attach them to the start of each source file to most effectively
291 | convey the exclusion of warranty; and each file should have at least
292 | the "copyright" line and a pointer to where the full notice is found.
293 |
294 |
295 | Copyright (C)
296 |
297 | This program is free software; you can redistribute it and/or modify
298 | it under the terms of the GNU General Public License as published by
299 | the Free Software Foundation; either version 2 of the License, or
300 | (at your option) any later version.
301 |
302 | This program is distributed in the hope that it will be useful,
303 | but WITHOUT ANY WARRANTY; without even the implied warranty of
304 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
305 | GNU General Public License for more details.
306 |
307 | You should have received a copy of the GNU General Public License along
308 | with this program; if not, write to the Free Software Foundation, Inc.,
309 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
310 |
311 | Also add information on how to contact you by electronic and paper mail.
312 |
313 | If the program is interactive, make it output a short notice like this
314 | when it starts in an interactive mode:
315 |
316 | Gnomovision version 69, Copyright (C) year name of author
317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
318 | This is free software, and you are welcome to redistribute it
319 | under certain conditions; type `show c' for details.
320 |
321 | The hypothetical commands `show w' and `show c' should show the appropriate
322 | parts of the General Public License. Of course, the commands you use may
323 | be called something other than `show w' and `show c'; they could even be
324 | mouse-clicks or menu items--whatever suits your program.
325 |
326 | You should also get your employer (if you work as a programmer) or your
327 | school, if any, to sign a "copyright disclaimer" for the program, if
328 | necessary. Here is a sample; alter the names:
329 |
330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
331 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
332 |
333 | {signature of Ty Coon}, 1 April 1989
334 | Ty Coon, President of Vice
335 |
336 | This General Public License does not permit incorporating your program into
337 | proprietary programs. If your program is a subroutine library, you may
338 | consider it more useful to permit linking proprietary applications with the
339 | library. If this is what you want to do, use the GNU Lesser General
340 | Public License instead of this License.
341 |
--------------------------------------------------------------------------------
/PresenceClient/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # CA1031: Do not catch general exception types
4 | dotnet_diagnostic.CA1031.severity = none
5 |
--------------------------------------------------------------------------------
/PresenceClient/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 |
33 | # Visual Studio 2015/2017 cache/options directory
34 | .vs/
35 | # Uncomment if you have tasks that create the project's static files in wwwroot
36 | #wwwroot/
37 |
38 | # Visual Studio 2017 auto generated files
39 | Generated\ Files/
40 |
41 | # MSTest test Results
42 | [Tt]est[Rr]esult*/
43 | [Bb]uild[Ll]og.*
44 |
45 | # NUnit
46 | *.VisualState.xml
47 | TestResult.xml
48 | nunit-*.xml
49 |
50 | # Build Results of an ATL Project
51 | [Dd]ebugPS/
52 | [Rr]eleasePS/
53 | dlldata.c
54 |
55 | # Benchmark Results
56 | BenchmarkDotNet.Artifacts/
57 |
58 | # .NET Core
59 | project.lock.json
60 | project.fragment.lock.json
61 | artifacts/
62 |
63 | # StyleCop
64 | StyleCopReport.xml
65 |
66 | # Files built by Visual Studio
67 | *_i.c
68 | *_p.c
69 | *_h.h
70 | *.ilk
71 | *.meta
72 | *.obj
73 | *.iobj
74 | *.pch
75 | *.pdb
76 | *.ipdb
77 | *.pgc
78 | *.pgd
79 | *.rsp
80 | *.sbr
81 | *.tlb
82 | *.tli
83 | *.tlh
84 | *.tmp
85 | *.tmp_proj
86 | *_wpftmp.csproj
87 | *.log
88 | *.vspscc
89 | *.vssscc
90 | .builds
91 | *.pidb
92 | *.svclog
93 | *.scc
94 |
95 | # Chutzpah Test files
96 | _Chutzpah*
97 |
98 | # Visual C++ cache files
99 | ipch/
100 | *.aps
101 | *.ncb
102 | *.opendb
103 | *.opensdf
104 | *.sdf
105 | *.cachefile
106 | *.VC.db
107 | *.VC.VC.opendb
108 |
109 | # Visual Studio profiler
110 | *.psess
111 | *.vsp
112 | *.vspx
113 | *.sap
114 |
115 | # Visual Studio Trace Files
116 | *.e2e
117 |
118 | # TFS 2012 Local Workspace
119 | $tf/
120 |
121 | # Guidance Automation Toolkit
122 | *.gpState
123 |
124 | # ReSharper is a .NET coding add-in
125 | _ReSharper*/
126 | *.[Rr]e[Ss]harper
127 | *.DotSettings.user
128 |
129 | # JustCode is a .NET coding add-in
130 | .JustCode
131 |
132 | # TeamCity is a build add-in
133 | _TeamCity*
134 |
135 | # DotCover is a Code Coverage Tool
136 | *.dotCover
137 |
138 | # AxoCover is a Code Coverage Tool
139 | .axoCover/*
140 | !.axoCover/settings.json
141 |
142 | # Visual Studio code coverage results
143 | *.coverage
144 | *.coveragexml
145 |
146 | # NCrunch
147 | _NCrunch_*
148 | .*crunch*.local.xml
149 | nCrunchTemp_*
150 |
151 | # MightyMoose
152 | *.mm.*
153 | AutoTest.Net/
154 |
155 | # Web workbench (sass)
156 | .sass-cache/
157 |
158 | # Installshield output folder
159 | [Ee]xpress/
160 |
161 | # DocProject is a documentation generator add-in
162 | DocProject/buildhelp/
163 | DocProject/Help/*.HxT
164 | DocProject/Help/*.HxC
165 | DocProject/Help/*.hhc
166 | DocProject/Help/*.hhk
167 | DocProject/Help/*.hhp
168 | DocProject/Help/Html2
169 | DocProject/Help/html
170 |
171 | # Click-Once directory
172 | publish/
173 |
174 | # Publish Web Output
175 | *.[Pp]ublish.xml
176 | *.azurePubxml
177 | # Note: Comment the next line if you want to checkin your web deploy settings,
178 | # but database connection strings (with potential passwords) will be unencrypted
179 | *.pubxml
180 | *.publishproj
181 |
182 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
183 | # checkin your Azure Web App publish settings, but sensitive information contained
184 | # in these scripts will be unencrypted
185 | PublishScripts/
186 |
187 | # NuGet Packages
188 | *.nupkg
189 | # NuGet Symbol Packages
190 | *.snupkg
191 | # The packages folder can be ignored because of Package Restore
192 | **/[Pp]ackages/*
193 | # except build/, which is used as an MSBuild target.
194 | !**/[Pp]ackages/build/
195 | # Uncomment if necessary however generally it will be regenerated when needed
196 | #!**/[Pp]ackages/repositories.config
197 | # NuGet v3's project.json files produces more ignorable files
198 | *.nuget.props
199 | *.nuget.targets
200 |
201 | # Microsoft Azure Build Output
202 | csx/
203 | *.build.csdef
204 |
205 | # Microsoft Azure Emulator
206 | ecf/
207 | rcf/
208 |
209 | # Windows Store app package directories and files
210 | AppPackages/
211 | BundleArtifacts/
212 | Package.StoreAssociation.xml
213 | _pkginfo.txt
214 | *.appx
215 | *.appxbundle
216 | *.appxupload
217 |
218 | # Visual Studio cache files
219 | # files ending in .cache can be ignored
220 | *.[Cc]ache
221 | # but keep track of directories ending in .cache
222 | !?*.[Cc]ache/
223 |
224 | # Others
225 | ClientBin/
226 | ~$*
227 | *~
228 | *.dbmdl
229 | *.dbproj.schemaview
230 | *.jfm
231 | *.pfx
232 | *.publishsettings
233 | orleans.codegen.cs
234 |
235 | # Including strong name files can present a security risk
236 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
237 | #*.snk
238 |
239 | # Since there are multiple workflows, uncomment next line to ignore bower_components
240 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
241 | #bower_components/
242 |
243 | # RIA/Silverlight projects
244 | Generated_Code/
245 |
246 | # Backup & report files from converting an old project file
247 | # to a newer Visual Studio version. Backup files are not needed,
248 | # because we have git ;-)
249 | _UpgradeReport_Files/
250 | Backup*/
251 | UpgradeLog*.XML
252 | UpgradeLog*.htm
253 | ServiceFabricBackup/
254 | *.rptproj.bak
255 |
256 | # SQL Server files
257 | *.mdf
258 | *.ldf
259 | *.ndf
260 |
261 | # Business Intelligence projects
262 | *.rdl.data
263 | *.bim.layout
264 | *.bim_*.settings
265 | *.rptproj.rsuser
266 | *- [Bb]ackup.rdl
267 | *- [Bb]ackup ([0-9]).rdl
268 | *- [Bb]ackup ([0-9][0-9]).rdl
269 |
270 | # Microsoft Fakes
271 | FakesAssemblies/
272 |
273 | # GhostDoc plugin setting file
274 | *.GhostDoc.xml
275 |
276 | # Node.js Tools for Visual Studio
277 | .ntvs_analysis.dat
278 | node_modules/
279 |
280 | # Visual Studio 6 build log
281 | *.plg
282 |
283 | # Visual Studio 6 workspace options file
284 | *.opt
285 |
286 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
287 | *.vbw
288 |
289 | # Visual Studio LightSwitch build output
290 | **/*.HTMLClient/GeneratedArtifacts
291 | **/*.DesktopClient/GeneratedArtifacts
292 | **/*.DesktopClient/ModelManifest.xml
293 | **/*.Server/GeneratedArtifacts
294 | **/*.Server/ModelManifest.xml
295 | _Pvt_Extensions
296 |
297 | # Paket dependency manager
298 | .paket/paket.exe
299 | paket-files/
300 |
301 | # FAKE - F# Make
302 | .fake/
303 |
304 | # CodeRush personal settings
305 | .cr/personal
306 |
307 | # Python Tools for Visual Studio (PTVS)
308 | __pycache__/
309 | *.pyc
310 |
311 | # Cake - Uncomment if you are using it
312 | # tools/**
313 | # !tools/packages.config
314 |
315 | # Tabs Studio
316 | *.tss
317 |
318 | # Telerik's JustMock configuration file
319 | *.jmconfig
320 |
321 | # BizTalk build output
322 | *.btp.cs
323 | *.btm.cs
324 | *.odx.cs
325 | *.xsd.cs
326 |
327 | # OpenCover UI analysis results
328 | OpenCover/
329 |
330 | # Azure Stream Analytics local run output
331 | ASALocalRun/
332 |
333 | # MSBuild Binary and Structured Log
334 | *.binlog
335 |
336 | # NVidia Nsight GPU debugger configuration file
337 | *.nvuser
338 |
339 | # MFractors (Xamarin productivity tool) working folder
340 | .mfractor/
341 |
342 | # Local History for Visual Studio
343 | .localhistory/
344 |
345 | # BeatPulse healthcheck temp database
346 | healthchecksdb
347 |
348 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
349 | MigrationBackup/
350 |
351 | # Ionide (cross platform F# VS Code tools) working folder
352 | .ionide/
353 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-CLI/ConsoleOptions.cs:
--------------------------------------------------------------------------------
1 | using CommandLine;
2 | using System.Net;
3 |
4 | namespace PresenceClient_CLI
5 | {
6 | public class ConsoleOptions
7 | {
8 | [Option('m', "ignore-home-screen", Required = false, Default = false, HelpText = "Don't display the home screen")]
9 | public bool IgnoreHomeScreen { get; set; }
10 |
11 | [Value(0, MetaName = "IP", Required = true, HelpText = "The IP address of your device")]
12 | public string IP { get; set; }
13 | public IPAddress ParsedIP { get; set; }
14 |
15 | [Value(1, MetaName = "Client ID", Required = true, HelpText = "The Client ID of your Discord Rich Presence application")]
16 | public ulong ClientID { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-CLI/Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunResearchInstitute/PresenceClient/1b8fb5d5e19f468ae550659727f92cf2051c3134/PresenceClient/PresenceClient-CLI/Icon.ico
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-CLI/PresenceClient-CLI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net5.0
6 | PresenceClient_CLI
7 | Icon.ico
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-CLI/Program.cs:
--------------------------------------------------------------------------------
1 | using CommandLine;
2 | using DiscordRPC;
3 | using PresenceCommon;
4 | using PresenceCommon.Types;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Net;
8 | using System.Net.Sockets;
9 | using System.Timers;
10 | using Timer = System.Timers.Timer;
11 |
12 | namespace PresenceClient_CLI
13 | {
14 | class Program
15 | {
16 | static Timer timer;
17 | static Socket client;
18 | static string LastProgramName = string.Empty;
19 | static Timestamps time = null;
20 | static DiscordRpcClient rpc;
21 | static ConsoleOptions Arguments;
22 |
23 | static int Main(string[] args)
24 | {
25 | AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
26 | Parser.Default.ParseArguments(args)
27 | .WithParsed(arguments =>
28 | {
29 | if (!IPAddress.TryParse(arguments.IP, out IPAddress iPAddress))
30 | {
31 | Console.WriteLine("Invalid IP");
32 | Environment.Exit(1);
33 | }
34 | arguments.ParsedIP = iPAddress;
35 | rpc = new DiscordRpcClient(arguments.ClientID.ToString());
36 | Arguments = arguments;
37 | })
38 | .WithNotParsed(errors => Environment.Exit(1));
39 |
40 | if (!rpc.Initialize())
41 | {
42 | Console.WriteLine("Unable to start RPC!");
43 | return 2;
44 | }
45 |
46 | IPEndPoint localEndPoint = new IPEndPoint(Arguments.ParsedIP, 0xCAFE);
47 |
48 | timer = new Timer()
49 | {
50 | Interval = 15000,
51 | Enabled = false,
52 | };
53 | timer.Elapsed += new ElapsedEventHandler(OnConnectTimeout);
54 |
55 | while (true)
56 | {
57 | client = new Socket(SocketType.Stream, ProtocolType.Tcp)
58 | {
59 | ReceiveTimeout = 5500,
60 | SendTimeout = 5500,
61 | };
62 |
63 | timer.Enabled = true;
64 |
65 | try
66 | {
67 | IAsyncResult result = client.BeginConnect(localEndPoint, null, null);
68 | bool success = result.AsyncWaitHandle.WaitOne(2000, true);
69 | if (!success)
70 | {
71 | //UpdateStatus("Could not connect to Server! Retrying...", Color.DarkRed);
72 | client.Close();
73 | }
74 | else
75 | {
76 | client.EndConnect(result);
77 | timer.Enabled = false;
78 |
79 | DataListen();
80 | }
81 | }
82 | catch (SocketException)
83 | {
84 | client.Close();
85 | if (rpc != null && !rpc.IsDisposed) rpc.ClearPresence();
86 | }
87 | }
88 | }
89 |
90 | private static void DataListen()
91 | {
92 | while (true)
93 | {
94 | try
95 | {
96 | byte[] bytes = Utils.ReceiveExactly(client);
97 |
98 | Title title = new Title(bytes);
99 | if (title.Magic == 0xffaadd23)
100 | {
101 | if (LastProgramName != title.Name)
102 | {
103 | time = Timestamps.Now;
104 | }
105 | if ((rpc != null && rpc.CurrentPresence == null) || LastProgramName != title.Name)
106 | {
107 | if (Arguments.IgnoreHomeScreen && title.Name == "Home Menu")
108 | {
109 | rpc.ClearPresence();
110 | }
111 | else
112 | {
113 | rpc.SetPresence(Utils.CreateDiscordPresence(title, time));
114 | }
115 | LastProgramName = title.Name;
116 | }
117 | }
118 | else
119 | {
120 | if (rpc != null && !rpc.IsDisposed) rpc.ClearPresence();
121 | client.Close();
122 | return;
123 | }
124 | }
125 | catch (SocketException)
126 | {
127 | if (rpc != null && !rpc.IsDisposed) rpc.ClearPresence();
128 | client.Close();
129 | return;
130 | }
131 | }
132 | }
133 |
134 | private static void OnConnectTimeout(object sender, ElapsedEventArgs e)
135 | {
136 | LastProgramName = string.Empty;
137 | time = null;
138 | }
139 |
140 | private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
141 | {
142 | if (client != null && client.Connected)
143 | client.Close();
144 |
145 | if(rpc != null && !rpc.IsDisposed)
146 | rpc.Dispose();
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # IDE0011: Add braces
4 | csharp_prefer_braces = false:silent
5 |
6 | # CA1815: Override equals and operator equals on value types
7 | dotnet_diagnostic.CA1815.severity = none
8 |
9 | # CA1034: Nested types should not be visible
10 | dotnet_diagnostic.CA1034.severity = none
11 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/Config.cs:
--------------------------------------------------------------------------------
1 | namespace PresenceClient_GUI
2 | {
3 | public class Config
4 | {
5 | public string IP, Client, BigKey, BigText, SmallKey, State;
6 | public bool DisplayTimer, AllowTray, DisplayMainMenu, SeenAutoMacPrompt, AutoToMac;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/MainForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace PresenceClient_GUI
2 | {
3 | partial class MainForm
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.components = new System.ComponentModel.Container();
32 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
33 | this.connectButton = new System.Windows.Forms.Button();
34 | this.clientBox = new System.Windows.Forms.TextBox();
35 | this.label1 = new System.Windows.Forms.Label();
36 | this.linkLabel1 = new System.Windows.Forms.LinkLabel();
37 | this.checkTime = new System.Windows.Forms.CheckBox();
38 | this.label2 = new System.Windows.Forms.Label();
39 | this.stateBox = new System.Windows.Forms.TextBox();
40 | this.label3 = new System.Windows.Forms.Label();
41 | this.smallKeyBox = new System.Windows.Forms.TextBox();
42 | this.label4 = new System.Windows.Forms.Label();
43 | this.bigKeyBox = new System.Windows.Forms.TextBox();
44 | this.label5 = new System.Windows.Forms.Label();
45 | this.bigTextBox = new System.Windows.Forms.TextBox();
46 | this.statusLabel = new System.Windows.Forms.Label();
47 | this.trayIcon = new System.Windows.Forms.NotifyIcon(this.components);
48 | this.trayContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
49 | this.connectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
50 | this.trayExitMenuItem = new System.Windows.Forms.ToolStripMenuItem();
51 | this.checkTray = new System.Windows.Forms.CheckBox();
52 | this.checkMainMenu = new System.Windows.Forms.CheckBox();
53 | this.addressBox = new System.Windows.Forms.TextBox();
54 | this.UseMacDefault = new System.Windows.Forms.CheckBox();
55 | this.trayContextMenu.SuspendLayout();
56 | this.SuspendLayout();
57 | //
58 | // connectButton
59 | //
60 | this.connectButton.Location = new System.Drawing.Point(88, 302);
61 | this.connectButton.Name = "connectButton";
62 | this.connectButton.Size = new System.Drawing.Size(75, 23);
63 | this.connectButton.TabIndex = 13;
64 | this.connectButton.Text = "Connect";
65 | this.connectButton.UseVisualStyleBackColor = true;
66 | this.connectButton.Click += new System.EventHandler(this.ConnectButton_Click);
67 | //
68 | // clientBox
69 | //
70 | this.clientBox.Location = new System.Drawing.Point(78, 73);
71 | this.clientBox.MaxLength = 18;
72 | this.clientBox.Name = "clientBox";
73 | this.clientBox.Size = new System.Drawing.Size(100, 20);
74 | this.clientBox.TabIndex = 3;
75 | //
76 | // label1
77 | //
78 | this.label1.AutoSize = true;
79 | this.label1.Location = new System.Drawing.Point(78, 18);
80 | this.label1.MinimumSize = new System.Drawing.Size(100, 0);
81 | this.label1.Name = "label1";
82 | this.label1.Size = new System.Drawing.Size(100, 13);
83 | this.label1.TabIndex = 0;
84 | this.label1.Text = "IP or MAC Address";
85 | this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
86 | //
87 | // linkLabel1
88 | //
89 | this.linkLabel1.AutoSize = true;
90 | this.linkLabel1.Location = new System.Drawing.Point(78, 58);
91 | this.linkLabel1.MinimumSize = new System.Drawing.Size(100, 0);
92 | this.linkLabel1.Name = "linkLabel1";
93 | this.linkLabel1.Size = new System.Drawing.Size(100, 13);
94 | this.linkLabel1.TabIndex = 16;
95 | this.linkLabel1.TabStop = true;
96 | this.linkLabel1.Text = "Client ID";
97 | this.linkLabel1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
98 | this.linkLabel1.VisitedLinkColor = System.Drawing.Color.Blue;
99 | this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.LinkLabel1_LinkClicked_1);
100 | //
101 | // checkTime
102 | //
103 | this.checkTime.AutoSize = true;
104 | this.checkTime.Checked = true;
105 | this.checkTime.CheckState = System.Windows.Forms.CheckState.Checked;
106 | this.checkTime.Location = new System.Drawing.Point(40, 331);
107 | this.checkTime.Name = "checkTime";
108 | this.checkTime.Size = new System.Drawing.Size(117, 17);
109 | this.checkTime.TabIndex = 14;
110 | this.checkTime.Text = "Show Time Lapsed";
111 | this.checkTime.UseVisualStyleBackColor = true;
112 | this.checkTime.CheckedChanged += new System.EventHandler(this.CheckTime_CheckedChanged);
113 | //
114 | // label2
115 | //
116 | this.label2.AutoSize = true;
117 | this.label2.Location = new System.Drawing.Point(78, 219);
118 | this.label2.MinimumSize = new System.Drawing.Size(100, 0);
119 | this.label2.Name = "label2";
120 | this.label2.Size = new System.Drawing.Size(100, 13);
121 | this.label2.TabIndex = 10;
122 | this.label2.Text = "State Text";
123 | this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
124 | //
125 | // stateBox
126 | //
127 | this.stateBox.Location = new System.Drawing.Point(78, 235);
128 | this.stateBox.MaxLength = 128;
129 | this.stateBox.Name = "stateBox";
130 | this.stateBox.Size = new System.Drawing.Size(100, 20);
131 | this.stateBox.TabIndex = 11;
132 | this.stateBox.TextChanged += new System.EventHandler(this.StateBox_TextChanged);
133 | //
134 | // label3
135 | //
136 | this.label3.AutoSize = true;
137 | this.label3.Location = new System.Drawing.Point(78, 180);
138 | this.label3.MinimumSize = new System.Drawing.Size(100, 0);
139 | this.label3.Name = "label3";
140 | this.label3.Size = new System.Drawing.Size(100, 13);
141 | this.label3.TabIndex = 8;
142 | this.label3.Text = "Small Image Key";
143 | this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
144 | //
145 | // smallKeyBox
146 | //
147 | this.smallKeyBox.Location = new System.Drawing.Point(78, 196);
148 | this.smallKeyBox.MaxLength = 32;
149 | this.smallKeyBox.Name = "smallKeyBox";
150 | this.smallKeyBox.Size = new System.Drawing.Size(100, 20);
151 | this.smallKeyBox.TabIndex = 9;
152 | this.smallKeyBox.TextChanged += new System.EventHandler(this.SKeyBox_TextChanged);
153 | //
154 | // label4
155 | //
156 | this.label4.AutoSize = true;
157 | this.label4.Location = new System.Drawing.Point(78, 96);
158 | this.label4.MinimumSize = new System.Drawing.Size(100, 0);
159 | this.label4.Name = "label4";
160 | this.label4.Size = new System.Drawing.Size(100, 13);
161 | this.label4.TabIndex = 4;
162 | this.label4.Text = "Large Image Key";
163 | this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
164 | //
165 | // bigKeyBox
166 | //
167 | this.bigKeyBox.Location = new System.Drawing.Point(78, 112);
168 | this.bigKeyBox.MaxLength = 32;
169 | this.bigKeyBox.Name = "bigKeyBox";
170 | this.bigKeyBox.Size = new System.Drawing.Size(100, 20);
171 | this.bigKeyBox.TabIndex = 5;
172 | this.bigKeyBox.TextChanged += new System.EventHandler(this.BigKeyBox_TextChanged);
173 | //
174 | // label5
175 | //
176 | this.label5.AutoSize = true;
177 | this.label5.Location = new System.Drawing.Point(78, 140);
178 | this.label5.MinimumSize = new System.Drawing.Size(100, 0);
179 | this.label5.Name = "label5";
180 | this.label5.Size = new System.Drawing.Size(100, 13);
181 | this.label5.TabIndex = 6;
182 | this.label5.Text = "Large Image Text";
183 | this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
184 | //
185 | // bigTextBox
186 | //
187 | this.bigTextBox.Location = new System.Drawing.Point(78, 156);
188 | this.bigTextBox.MaxLength = 128;
189 | this.bigTextBox.Name = "bigTextBox";
190 | this.bigTextBox.Size = new System.Drawing.Size(100, 20);
191 | this.bigTextBox.TabIndex = 7;
192 | this.bigTextBox.TextChanged += new System.EventHandler(this.BigTextBox_TextChanged);
193 | //
194 | // statusLabel
195 | //
196 | this.statusLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
197 | this.statusLabel.ForeColor = System.Drawing.Color.Red;
198 | this.statusLabel.Location = new System.Drawing.Point(28, 258);
199 | this.statusLabel.Name = "statusLabel";
200 | this.statusLabel.Size = new System.Drawing.Size(200, 40);
201 | this.statusLabel.TabIndex = 12;
202 | this.statusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
203 | //
204 | // trayIcon
205 | //
206 | this.trayIcon.ContextMenuStrip = this.trayContextMenu;
207 | this.trayIcon.Icon = ((System.Drawing.Icon)(resources.GetObject("trayIcon.Icon")));
208 | this.trayIcon.Text = "PresenceClient (Disconnected)";
209 | this.trayIcon.Visible = true;
210 | this.trayIcon.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.TrayIcon_MouseDoubleClick);
211 | //
212 | // trayContextMenu
213 | //
214 | this.trayContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
215 | this.connectToolStripMenuItem,
216 | this.trayExitMenuItem});
217 | this.trayContextMenu.Name = "trayContextMenu";
218 | this.trayContextMenu.Size = new System.Drawing.Size(120, 48);
219 | //
220 | // connectToolStripMenuItem
221 | //
222 | this.connectToolStripMenuItem.Name = "connectToolStripMenuItem";
223 | this.connectToolStripMenuItem.Size = new System.Drawing.Size(119, 22);
224 | this.connectToolStripMenuItem.Text = "Connect";
225 | this.connectToolStripMenuItem.Click += new System.EventHandler(this.ConnectButton_Click);
226 | //
227 | // trayExitMenuItem
228 | //
229 | this.trayExitMenuItem.Name = "trayExitMenuItem";
230 | this.trayExitMenuItem.Size = new System.Drawing.Size(119, 22);
231 | this.trayExitMenuItem.Text = "Exit";
232 | this.trayExitMenuItem.Click += new System.EventHandler(this.TrayExitMenuItem_Click);
233 | //
234 | // checkTray
235 | //
236 | this.checkTray.AutoSize = true;
237 | this.checkTray.Location = new System.Drawing.Point(40, 354);
238 | this.checkTray.Name = "checkTray";
239 | this.checkTray.Size = new System.Drawing.Size(102, 17);
240 | this.checkTray.TabIndex = 15;
241 | this.checkTray.Text = "Minimize to Tray";
242 | this.checkTray.UseVisualStyleBackColor = true;
243 | //
244 | // checkMainMenu
245 | //
246 | this.checkMainMenu.AutoSize = true;
247 | this.checkMainMenu.Checked = true;
248 | this.checkMainMenu.CheckState = System.Windows.Forms.CheckState.Checked;
249 | this.checkMainMenu.Location = new System.Drawing.Point(40, 377);
250 | this.checkMainMenu.Name = "checkMainMenu";
251 | this.checkMainMenu.Size = new System.Drawing.Size(175, 17);
252 | this.checkMainMenu.TabIndex = 18;
253 | this.checkMainMenu.Text = "Display Home Menu as a status";
254 | this.checkMainMenu.UseVisualStyleBackColor = true;
255 | this.checkMainMenu.CheckedChanged += new System.EventHandler(this.CheckMainMenu_CheckedChanged);
256 | //
257 | // addressBox
258 | //
259 | this.addressBox.Location = new System.Drawing.Point(78, 35);
260 | this.addressBox.Name = "addressBox";
261 | this.addressBox.Size = new System.Drawing.Size(100, 20);
262 | this.addressBox.TabIndex = 1;
263 | //
264 | // UseMacDefault
265 | //
266 | this.UseMacDefault.AutoSize = true;
267 | this.UseMacDefault.Location = new System.Drawing.Point(40, 401);
268 | this.UseMacDefault.Name = "UseMacDefault";
269 | this.UseMacDefault.Size = new System.Drawing.Size(178, 17);
270 | this.UseMacDefault.TabIndex = 19;
271 | this.UseMacDefault.Text = "Automatically convert IP to MAC";
272 | this.UseMacDefault.UseVisualStyleBackColor = true;
273 | this.UseMacDefault.CheckedChanged += new System.EventHandler(this.UseMacDefault_CheckedChanged);
274 | //
275 | // MainForm
276 | //
277 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
278 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
279 | this.ClientSize = new System.Drawing.Size(256, 437);
280 | this.Controls.Add(this.UseMacDefault);
281 | this.Controls.Add(this.checkMainMenu);
282 | this.Controls.Add(this.checkTray);
283 | this.Controls.Add(this.statusLabel);
284 | this.Controls.Add(this.label5);
285 | this.Controls.Add(this.bigTextBox);
286 | this.Controls.Add(this.label4);
287 | this.Controls.Add(this.bigKeyBox);
288 | this.Controls.Add(this.label3);
289 | this.Controls.Add(this.smallKeyBox);
290 | this.Controls.Add(this.label2);
291 | this.Controls.Add(this.stateBox);
292 | this.Controls.Add(this.checkTime);
293 | this.Controls.Add(this.linkLabel1);
294 | this.Controls.Add(this.label1);
295 | this.Controls.Add(this.clientBox);
296 | this.Controls.Add(this.connectButton);
297 | this.Controls.Add(this.addressBox);
298 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
299 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
300 | this.MaximizeBox = false;
301 | this.Name = "MainForm";
302 | this.Text = "PresenceClient";
303 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
304 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.MainForm_FormClosed);
305 | this.Load += new System.EventHandler(this.MainForm_Load);
306 | this.trayContextMenu.ResumeLayout(false);
307 | this.ResumeLayout(false);
308 | this.PerformLayout();
309 |
310 | }
311 |
312 | #endregion
313 | private System.Windows.Forms.Button connectButton;
314 | private System.Windows.Forms.TextBox clientBox;
315 | private System.Windows.Forms.Label label1;
316 | private System.Windows.Forms.LinkLabel linkLabel1;
317 | private System.Windows.Forms.CheckBox checkTime;
318 | private System.Windows.Forms.Label label2;
319 | private System.Windows.Forms.TextBox stateBox;
320 | private System.Windows.Forms.Label label3;
321 | private System.Windows.Forms.TextBox smallKeyBox;
322 | private System.Windows.Forms.Label label4;
323 | private System.Windows.Forms.TextBox bigKeyBox;
324 | private System.Windows.Forms.Label label5;
325 | private System.Windows.Forms.TextBox bigTextBox;
326 | private System.Windows.Forms.Label statusLabel;
327 | private System.Windows.Forms.NotifyIcon trayIcon;
328 | private System.Windows.Forms.ContextMenuStrip trayContextMenu;
329 | private System.Windows.Forms.ToolStripMenuItem trayExitMenuItem;
330 | private System.Windows.Forms.CheckBox checkTray;
331 | private System.Windows.Forms.ToolStripMenuItem connectToolStripMenuItem;
332 | private System.Windows.Forms.CheckBox checkMainMenu;
333 | private System.Windows.Forms.TextBox addressBox;
334 | private System.Windows.Forms.CheckBox UseMacDefault;
335 | }
336 | }
337 |
338 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/MainForm.cs:
--------------------------------------------------------------------------------
1 | using DiscordRPC;
2 | #if DEBUG
3 | using DiscordRPC.Logging;
4 | #endif
5 | using Newtonsoft.Json;
6 | using PresenceCommon.Types;
7 | using PresenceClient_GUI.Properties;
8 | using System;
9 | using System.Diagnostics;
10 | using System.Drawing;
11 | using System.IO;
12 | using System.Media;
13 | using System.Net;
14 | using System.Net.Sockets;
15 | using System.Threading;
16 | using System.Timers;
17 | using System.Windows.Forms;
18 | using Timer = System.Timers.Timer;
19 |
20 | namespace PresenceClient_GUI
21 | {
22 | public partial class MainForm : Form
23 | {
24 | private Thread listenThread;
25 | private static Socket client;
26 | private static DiscordRpcClient rpc;
27 | private IPAddress ipAddress;
28 | private bool ManualUpdate = false;
29 | private string LastProgramName = string.Empty;
30 | private Timestamps time = null;
31 | private static Timer timer;
32 | private bool HasSeenMacPrompt = false;
33 |
34 | public MainForm()
35 | {
36 | InitializeComponent();
37 | listenThread = new Thread(TryConnect);
38 | }
39 |
40 | private void ConnectButton_Click(object sender, EventArgs e)
41 | {
42 | if (connectButton.Text == "Connect")
43 | {
44 | // Check and see if ClientID is empty
45 | if (string.IsNullOrWhiteSpace(clientBox.Text))
46 | {
47 | Show();
48 | Activate();
49 | UpdateStatus("Client ID cannot be empty", Color.DarkRed);
50 | SystemSounds.Exclamation.Play();
51 | return;
52 | }
53 |
54 | // Check and see if we have an IP
55 | // If we have an IP, prompt to swap to MAC Address
56 | if (IPAddress.TryParse(addressBox.Text, out ipAddress))
57 | {
58 | if (!HasSeenMacPrompt)
59 | {
60 | HasSeenMacPrompt = true;
61 |
62 | string message = "We've detected that you're using an IP to connect to your device. Connecting via MAC address may make it easier to reconnect to your device in case the IP changes." +
63 | "\n\nWould you like to swap to connecting via MAC address? \n(We'll only ask this once.)";
64 |
65 | if (MessageBox.Show(message, "IP Detected", MessageBoxButtons.YesNo) == DialogResult.Yes)
66 | {
67 | UseMacDefault.Checked = true;
68 | IpToMac();
69 | }
70 | else
71 | UseMacDefault.Checked = false;
72 | }
73 | else if (UseMacDefault.Checked == true)
74 | IpToMac();
75 | }
76 | else
77 | {
78 | // If in this block, means we dont have a valid IP.
79 | // Check and see if it's a MAC Address
80 | try
81 | {
82 | IPAddress.TryParse(Utils.GetIpByMac(addressBox.Text), out ipAddress);
83 | }
84 | catch (FormatException)
85 | {
86 | Show();
87 | Activate();
88 | UpdateStatus("Invalid IP or MAC Address", Color.DarkRed);
89 | SystemSounds.Exclamation.Play();
90 | return;
91 | }
92 | }
93 |
94 | listenThread.Start();
95 |
96 | connectButton.Text = "Disconnect";
97 | connectToolStripMenuItem.Text = "Disconnect";
98 |
99 | addressBox.Enabled = false;
100 | clientBox.Enabled = false;
101 | }
102 | else
103 | {
104 | listenThread.Abort();
105 | if (rpc != null && !rpc.IsDisposed)
106 | {
107 | rpc.ClearPresence();
108 | rpc.Dispose();
109 | }
110 |
111 | if (client != null) client.Close();
112 | if (timer != null) timer.Dispose();
113 | listenThread = new Thread(TryConnect);
114 | UpdateStatus("", Color.Gray);
115 | connectButton.Text = "Connect";
116 | connectToolStripMenuItem.Text = "Connect";
117 | trayIcon.Icon = Resources.Disconnected;
118 | trayIcon.Text = "PresenceClient (Disconnected)";
119 |
120 | ipAddress = null;
121 | addressBox.Enabled = true;
122 | clientBox.Enabled = true;
123 | LastProgramName = string.Empty;
124 | time = null;
125 | }
126 | }
127 |
128 | private void OnConnectTimeout(object source, ElapsedEventArgs e)
129 | {
130 | LastProgramName = string.Empty;
131 | time = null;
132 | }
133 |
134 | private void TryConnect()
135 | {
136 | if (rpc != null && !rpc.IsDisposed)
137 | {
138 | rpc.ClearPresence();
139 | rpc.Dispose();
140 | }
141 |
142 | rpc = new DiscordRpcClient(clientBox.Text);
143 | rpc.Initialize();
144 |
145 | //Create a timer that will be enabled when we lose connection to the server.
146 | //Once the full time has passed, it will clear the info it had of the previous game
147 | timer = new Timer()
148 | {
149 | Interval = 60000,
150 | SynchronizingObject = this,
151 | Enabled = false,
152 | };
153 | timer.Elapsed += new ElapsedEventHandler(OnConnectTimeout);
154 |
155 | #if DEBUG
156 | rpc.Logger = new ConsoleLogger() { Level = LogLevel.Warning };
157 | //Subscribe to events
158 | rpc.OnReady += (s, obj) =>
159 | {
160 | Console.WriteLine("Received Ready from user {0}", obj.User.Username);
161 | };
162 |
163 | rpc.OnPresenceUpdate += (s, obj) =>
164 | {
165 | Console.WriteLine("Received Update! {0}", obj.Presence);
166 | };
167 | #endif
168 |
169 | while (true)
170 | {
171 | client = new Socket(SocketType.Stream, ProtocolType.Tcp)
172 | {
173 | ReceiveTimeout = 5500,
174 | SendTimeout = 5500,
175 | };
176 |
177 | UpdateStatus("Attemping to connect to server...", Color.Gray);
178 | trayIcon.Icon = Resources.Disconnected;
179 | trayIcon.Text = "PresenceClient (Connecting...)";
180 | timer.Enabled = true;
181 |
182 | try
183 | {
184 | IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 0xCAFE);
185 |
186 | IAsyncResult result = client.BeginConnect(localEndPoint, null, null);
187 | bool success = result.AsyncWaitHandle.WaitOne(2000, true);
188 | if (!success)
189 | {
190 | //UpdateStatus("Could not connect to Server! Retrying...", Color.DarkRed);
191 | client.Close();
192 | }
193 | else
194 | {
195 | client.EndConnect(result);
196 | timer.Enabled = false;
197 |
198 | DataListen();
199 | }
200 | }
201 | catch (ArgumentNullException)
202 | {
203 | //The ip address is null because arp couldn't find the target mac address.
204 | //So we sleep and search for it again.
205 | Thread.Sleep(1000);
206 | IPAddress.TryParse(Utils.GetIpByMac(addressBox.Text), out ipAddress);
207 | }
208 | catch (SocketException)
209 | {
210 | client.Close();
211 | if (rpc != null && !rpc.IsDisposed) rpc.ClearPresence();
212 | }
213 | }
214 | }
215 |
216 | private void DataListen()
217 | {
218 | ManualUpdate = true;
219 | while (true)
220 | {
221 | try
222 | {
223 | byte[] bytes = PresenceCommon.Utils.ReceiveExactly(client);
224 | UpdateStatus("Connected to the server!", Color.Green);
225 | trayIcon.Icon = Resources.Connected;
226 | trayIcon.Text = "PresenceClient (Connected)";
227 |
228 | Title title = new Title(bytes);
229 | if (title.Magic == 0xffaadd23)
230 | {
231 | if (LastProgramName != title.Name)
232 | {
233 | time = Timestamps.Now;
234 | }
235 | if ((LastProgramName != title.Name) || ManualUpdate)
236 | {
237 | if (rpc != null)
238 | {
239 |
240 | if (!checkMainMenu.Checked && title.Name == "Home Menu")
241 | rpc.ClearPresence();
242 | else
243 | {
244 | rpc.SetPresence(PresenceCommon.Utils.CreateDiscordPresence(title, time, bigKeyBox.Text, bigTextBox.Text, smallKeyBox.Text, stateBox.Text, checkTime.Checked));
245 | }
246 | }
247 | ManualUpdate = false;
248 | LastProgramName = title.Name;
249 | }
250 | }
251 | else
252 | {
253 | if (rpc != null && !rpc.IsDisposed) rpc.ClearPresence();
254 | client.Close();
255 | return;
256 | }
257 | }
258 | catch (SocketException)
259 | {
260 | if (rpc != null && !rpc.IsDisposed) rpc.ClearPresence();
261 | client.Close();
262 | return;
263 | }
264 | }
265 | }
266 |
267 | private void IpToMac()
268 | {
269 | string macAddress = Utils.GetMacByIp(ipAddress.ToString());
270 | if (macAddress != null)
271 | addressBox.Text = macAddress;
272 | else
273 | MessageBox.Show("Can't convert to MAC Address! Sorry!");
274 | }
275 |
276 | private void MainForm_Load(object sender, EventArgs e)
277 | {
278 | if (File.Exists("Config.json"))
279 | {
280 | Config cfg = JsonConvert.DeserializeObject(File.ReadAllText("Config.json"));
281 | checkTime.Checked = cfg.DisplayTimer;
282 | bigKeyBox.Text = cfg.BigKey;
283 | bigTextBox.Text = cfg.BigText;
284 | smallKeyBox.Text = cfg.SmallKey;
285 | addressBox.Text = cfg.IP;
286 | stateBox.Text = cfg.State;
287 | clientBox.Text = cfg.Client;
288 | checkTray.Checked = cfg.AllowTray;
289 | checkMainMenu.Checked = cfg.DisplayMainMenu;
290 | HasSeenMacPrompt = cfg.SeenAutoMacPrompt;
291 | UseMacDefault.Checked = cfg.AutoToMac;
292 | }
293 | }
294 |
295 | private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
296 | {
297 | listenThread.Abort();
298 | if (rpc != null && !rpc.IsDisposed)
299 | {
300 | rpc.ClearPresence();
301 | rpc.Dispose();
302 | }
303 |
304 | if (client != null) client.Close();
305 | }
306 |
307 | private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
308 | {
309 | if (e.CloseReason == CloseReason.UserClosing && checkTray.Checked)
310 | {
311 | e.Cancel = true;
312 | Hide();
313 | }
314 | else
315 | {
316 | if (timer != null) timer.Dispose();
317 |
318 | Config cfg = new Config()
319 | {
320 | IP = addressBox.Text,
321 | Client = clientBox.Text,
322 | BigKey = bigKeyBox.Text,
323 | SmallKey = smallKeyBox.Text,
324 | State = stateBox.Text,
325 | BigText = bigTextBox.Text,
326 | DisplayTimer = checkTime.Checked,
327 | AllowTray = checkTray.Checked,
328 | DisplayMainMenu = checkMainMenu.Checked,
329 | SeenAutoMacPrompt = HasSeenMacPrompt,
330 | AutoToMac = UseMacDefault.Checked
331 | };
332 | File.WriteAllText("Config.json", JsonConvert.SerializeObject(cfg, Formatting.Indented));
333 | }
334 | }
335 |
336 | private void TrayIcon_MouseDoubleClick(object sender, MouseEventArgs e)
337 | {
338 | Show();
339 | Activate();
340 | }
341 |
342 | private void UpdateStatus(string text, Color color)
343 | {
344 | MethodInvoker inv = () =>
345 | {
346 | statusLabel.Text = text;
347 | statusLabel.ForeColor = color;
348 | };
349 | Invoke(inv);
350 | }
351 |
352 | private void CheckTime_CheckedChanged(object sender, EventArgs e) => ManualUpdate = true;
353 |
354 | private void BigKeyBox_TextChanged(object sender, EventArgs e) => ManualUpdate = true;
355 |
356 | private void SKeyBox_TextChanged(object sender, EventArgs e) => ManualUpdate = true;
357 |
358 | private void StateBox_TextChanged(object sender, EventArgs e) => ManualUpdate = true;
359 |
360 | private void BigTextBox_TextChanged(object sender, EventArgs e) => ManualUpdate = true;
361 |
362 | private void TrayExitMenuItem_Click(object sender, EventArgs e) => Application.Exit();
363 |
364 | private void LinkLabel1_LinkClicked_1(object sender, LinkLabelLinkClickedEventArgs e) => Process.Start($"https://discordapp.com/developers/applications/{clientBox.Text}");
365 |
366 | private void CheckMainMenu_CheckedChanged(object sender, EventArgs e) => ManualUpdate = true;
367 |
368 | private void UseMacDefault_CheckedChanged(object sender, EventArgs e) => HasSeenMacPrompt = true;
369 | }
370 | }
371 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/PresenceClient-GUI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {E1E88510-EE21-4E59-A5FD-052063CE7AC3}
8 | WinExe
9 | PresenceClient_GUI
10 | PresenceClient-GUI
11 | v4.8
12 | 512
13 | false
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 | Resources\Icon.ico
37 |
38 |
39 |
40 |
41 |
42 |
43 | ..\packages\DiscordRichPresence.1.0.166\lib\net35\DiscordRPC.dll
44 |
45 |
46 | ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | Form
61 |
62 |
63 | MainForm.cs
64 |
65 |
66 |
67 |
68 | True
69 | True
70 | Resources.resx
71 |
72 |
73 |
74 | MainForm.cs
75 |
76 |
77 | ResXFileCodeGenerator
78 | Designer
79 | Resources.Designer.cs
80 |
81 |
82 |
83 |
84 | SettingsSingleFileGenerator
85 | Settings.Designer.cs
86 |
87 |
88 | True
89 | Settings.settings
90 | True
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | {73ead2e4-0f1a-4d06-a84d-13c2ebaa03d8}
104 | PresenceCommon
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace PresenceClient_GUI
5 | {
6 | static class Program
7 | {
8 | ///
9 | /// The main entry point for the application.
10 | ///
11 | [STAThread]
12 | static void Main()
13 | {
14 | Application.EnableVisualStyles();
15 | Application.SetCompatibleTextRenderingDefault(false);
16 | Application.Run(new MainForm());
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("PresenceClient")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("PresenceClient")]
12 | [assembly: AssemblyCopyright("Copyright © 2020")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("e1e88510-ee21-4e59-a5fd-052063ce7ac3")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace PresenceClient_GUI.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PresenceClient_GUI.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
65 | ///
66 | internal static System.Drawing.Icon Connected {
67 | get {
68 | object obj = ResourceManager.GetObject("Connected", resourceCulture);
69 | return ((System.Drawing.Icon)(obj));
70 | }
71 | }
72 |
73 | ///
74 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
75 | ///
76 | internal static System.Drawing.Icon Disconnected {
77 | get {
78 | object obj = ResourceManager.GetObject("Disconnected", resourceCulture);
79 | return ((System.Drawing.Icon)(obj));
80 | }
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\Resources\Connected.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
123 |
124 |
125 | ..\Resources\Disconnected.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
126 |
127 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace PresenceClient_GUI.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/Resources/Connected.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunResearchInstitute/PresenceClient/1b8fb5d5e19f468ae550659727f92cf2051c3134/PresenceClient/PresenceClient-GUI/Resources/Connected.ico
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/Resources/Disconnected.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunResearchInstitute/PresenceClient/1b8fb5d5e19f468ae550659727f92cf2051c3134/PresenceClient/PresenceClient-GUI/Resources/Disconnected.ico
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/Resources/Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SunResearchInstitute/PresenceClient/1b8fb5d5e19f468ae550659727f92cf2051c3134/PresenceClient/PresenceClient-GUI/Resources/Icon.ico
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/Utils.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics;
3 | using System.Linq;
4 | using System.Text.RegularExpressions;
5 |
6 | namespace PresenceClient_GUI
7 | {
8 | public static class Utils
9 | {
10 | public struct MacIpPair
11 | {
12 | public string MacAddress;
13 | public string IpAddress;
14 | }
15 |
16 | //This will only work on Windows(?)
17 | public static string GetMacByIp(string ip)
18 | {
19 | List macIpPairs = GetAllMacAddressesAndIPPairs();
20 |
21 | return macIpPairs.FirstOrDefault(x => x.IpAddress == ip).MacAddress ?? "";
22 | }
23 |
24 | public static string GetIpByMac(string mac)
25 | {
26 | mac = mac.ToLower();
27 | List macIpPairs = GetAllMacAddressesAndIPPairs();
28 |
29 | return macIpPairs.FirstOrDefault(x => x.MacAddress == mac).IpAddress ?? "";
30 | }
31 | public static List GetAllMacAddressesAndIPPairs()
32 | {
33 | List mip = new List();
34 | using (Process pProcess = new Process())
35 | {
36 | pProcess.StartInfo.FileName = "arp";
37 | pProcess.StartInfo.Arguments = "-a ";
38 | pProcess.StartInfo.UseShellExecute = false;
39 | pProcess.StartInfo.RedirectStandardOutput = true;
40 | pProcess.StartInfo.CreateNoWindow = true;
41 | pProcess.Start();
42 | string cmdOutput = pProcess.StandardOutput.ReadToEnd();
43 | string pattern = @"(?([0-9]{1,3}\.?){4})\s*(?([a-f0-9]{2}-?){6})";
44 |
45 | foreach (Match m in Regex.Matches(cmdOutput, pattern, RegexOptions.IgnoreCase))
46 | {
47 | mip.Add(new MacIpPair()
48 | {
49 | MacAddress = m.Groups["mac"].Value,
50 | IpAddress = m.Groups["ip"].Value
51 | });
52 | }
53 | }
54 | return mip;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-GUI/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-Py/README.md:
--------------------------------------------------------------------------------
1 | # Running with Python
2 | [](https://github.com/qwertyquerty/pypresence)
3 |
4 | # Requirements
5 | Follow [setup](https://github.com/Sun-Research-University/PresenceClient/blob/master/README.md) found here
6 |
7 | Download and install the [latest version of Python](https://www.python.org/downloads/) for your platform
8 | ### Use pip to install requirements
9 | Just run the following command
10 | ```sh
11 | pip install pypresence
12 | ```
13 | :warning: **If you plan on running this headlessly,** be aware for any rich presence application to work, the client must also be running an instance of the [Discord](https://discord.com/download) client.
14 |
15 | # Usage
16 | Download the latest ```presence-client.py``` file in the [Releases](https://github.com/Sun-Research-University/PresenceClient/releases) tab
17 |
18 | Then just run the following command in the same directory as your ```presence-client.py``` file
19 | ```sh
20 | python presence-client.py (arguments...)
21 | ```
22 | ### Arguments
23 | `ip` The IP address of your device.
24 |
25 | `client_id` The Client ID of your Discord Rich Presence application.
26 |
27 | `--ignore-home-screen` Don't display the home screen.
28 |
29 | Run the help command with `-h` or `--help`
30 |
31 | ```sh
32 | usage: presence-client.py [-h] [--ignore-home-screen] ip client_id
33 |
34 | positional arguments:
35 | ip The IP address of your device.
36 | client_id The Client ID of your Discord Rich Presence application.
37 |
38 | optional arguments:
39 | -h, --help show this help message and exit
40 | --ignore-home-screen Dont display the home screen. Defaults to false if missing this flag.
41 | ```
42 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient-Py/presence-client.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import sys
3 | import json
4 | import socket
5 | import struct
6 | import time
7 | import re
8 | import requests
9 | from pypresence import Presence
10 |
11 | TCP_PORT = 0xCAFE
12 | PACKETMAGIC = 0xFFAADD23
13 |
14 | parser = argparse.ArgumentParser()
15 | parser.add_argument('ip', help='The IP address of your device')
16 | parser.add_argument('client_id', help='The Client ID of your Discord Rich Presence application')
17 | parser.add_argument('--ignore-home-screen', dest='ignore_home_screen', action='store_true', help='Don\'t display the home screen. Defaults to false if missing this flag.')
18 |
19 | questOverrides = None
20 | switchOverrides = None
21 |
22 | try:
23 | questOverrides = json.loads(requests.get("https://raw.githubusercontent.com/Sun-Research-University/PresenceClient/master/Resource/QuestApplicationOverrides.json").text)
24 | switchOverrides = json.loads(requests.get("https://raw.githubusercontent.com/Sun-Research-University/PresenceClient/master/Resource/SwitchApplicationOverrides.json").text)
25 | except:
26 | print('Failed to retrieve Override files')
27 | exit()
28 |
29 | #Defines a title packet
30 | class Title:
31 |
32 | def __init__(self, raw_data):
33 | unpacker = struct.Struct('2L612s')
34 | enc_data = unpacker.unpack(raw_data)
35 | self.magic = int(enc_data[0])
36 | if int(enc_data[1]) == 0:
37 | self.pid = int(enc_data[1])
38 | self.name = 'Home Menu'
39 | else:
40 | self.pid = int(enc_data[1])
41 | self.name = enc_data[2].decode('utf-8', 'ignore').split('\x00')[0]
42 | if int(enc_data[0]) == PACKETMAGIC:
43 | if self.name in questOverrides:
44 | if questOverrides[self.name]['CustomName'] != '':
45 | self.name = questOverrides[self.name]['CustomName']
46 | else:
47 | if self.name in switchOverrides:
48 | if switchOverrides[self.name]['CustomName'] != '':
49 | self.name = switchOverrides[self.name]['CustomName']
50 |
51 |
52 | def main():
53 | consoleargs = parser.parse_args()
54 |
55 | switch_ip = consoleargs.ip
56 | client_id = consoleargs.client_id
57 |
58 | if not checkIP(switch_ip):
59 | print('Invalid IP')
60 | exit()
61 |
62 | rpc = Presence(str(client_id))
63 | try:
64 | rpc.connect()
65 | rpc.clear()
66 | except:
67 | print('Unable to start RPC!')
68 |
69 | switch_server_address = (switch_ip, TCP_PORT)
70 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
71 |
72 | try:
73 | sock.connect(switch_server_address)
74 | print('Successfully connected to %s' % switch_ip + ':' + str(TCP_PORT))
75 | except:
76 | print('Error connection to %s refused' % switch_ip + ':' + str(TCP_PORT))
77 | exit()
78 |
79 | lastProgramName = ''
80 | startTimer = 0
81 |
82 | while True:
83 | data = None
84 | try:
85 | data = sock.recv(628)
86 | except:
87 | print('Could not connect to Server! Retrying...')
88 | startTimer = 0
89 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
90 | try:
91 | sock.connect(switch_server_address)
92 | print('Successfully reconnected to %s' %
93 | repr(switch_server_address))
94 | except:
95 | print('Error reconnection to %s refused' %
96 | repr(switch_server_address))
97 | exit()
98 | title = Title(data)
99 | if title.magic == PACKETMAGIC:
100 | if lastProgramName != title.name:
101 | startTimer = int(time.time())
102 | if consoleargs.ignore_home_screen and title.name == 'Home Menu':
103 | rpc.clear()
104 | else:
105 | smallimagetext = ''
106 | largeimagekey = ''
107 | details = ''
108 | largeimagetext = title.name
109 | if int(title.pid) != PACKETMAGIC:
110 | smallimagetext = 'SwitchPresence-Rewritten'
111 | if title.name not in switchOverrides:
112 | largeimagekey = iconFromPid(title.pid)
113 | details = 'Playing ' + str(title.name)
114 | else:
115 | orinfo = switchOverrides[title.name]
116 | largeimagekey = orinfo['CustomKey'] or iconFromPid(title.pid)
117 | details = orinfo['CustomPrefix'] or 'Playing'
118 | details += ' ' + title.name
119 | else:
120 | smallimagetext = 'QuestPresence'
121 | if title.name not in questOverrides:
122 | largeimagekey = title.name.lower().replace(' ', '')
123 | details = 'Playing ' + title.name
124 | else:
125 | orinfo = questOverrides[title.name]
126 | largeimagekey = orinfo['CustomKey'] or title.name.lower().replace(
127 | ' ', '')
128 | details = orinfo['CustomPrefix'] or 'Playing'
129 | details += ' ' + title.name
130 | if not title.name:
131 | title.name = ''
132 | lastProgramName = title.name
133 | rpc.update(details=details, start=startTimer, large_image=largeimagekey,
134 | large_text=largeimagetext, small_text=smallimagetext)
135 | time.sleep(1)
136 | else:
137 | time.sleep(1)
138 | rpc.clear()
139 | rpc.close()
140 | sock.close()
141 | exit()
142 |
143 | # uses regex to validate ip
144 | def checkIP(ip):
145 | regex = r'''^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\.(
146 | 25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\.(
147 | 25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\.(
148 | 25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)$'''
149 | return re.search(regex, ip)
150 |
151 | def iconFromPid(pid):
152 | return '0' + str(hex(int(pid))).split('0x')[1]
153 |
154 |
155 | if __name__ == '__main__':
156 | main()
--------------------------------------------------------------------------------
/PresenceClient/PresenceClient.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29201.188
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PresenceClient-GUI", "PresenceClient-GUI\PresenceClient-GUI.csproj", "{E1E88510-EE21-4E59-A5FD-052063CE7AC3}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PresenceClient-CLI", "PresenceClient-CLI\PresenceClient-CLI.csproj", "{7CDBBC94-5E21-474D-ABF9-AF4320E3EC1D}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PresenceCommon", "PresenceCommon\PresenceCommon.csproj", "{73EAD2E4-0F1A-4D06-A84D-13C2EBAA03D8}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {E1E88510-EE21-4E59-A5FD-052063CE7AC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {E1E88510-EE21-4E59-A5FD-052063CE7AC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {E1E88510-EE21-4E59-A5FD-052063CE7AC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {E1E88510-EE21-4E59-A5FD-052063CE7AC3}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {7CDBBC94-5E21-474D-ABF9-AF4320E3EC1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {7CDBBC94-5E21-474D-ABF9-AF4320E3EC1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {7CDBBC94-5E21-474D-ABF9-AF4320E3EC1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {7CDBBC94-5E21-474D-ABF9-AF4320E3EC1D}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {73EAD2E4-0F1A-4D06-A84D-13C2EBAA03D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {73EAD2E4-0F1A-4D06-A84D-13C2EBAA03D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {73EAD2E4-0F1A-4D06-A84D-13C2EBAA03D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {73EAD2E4-0F1A-4D06-A84D-13C2EBAA03D8}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {930A04E8-1CA7-4524-A232-C64B2376520A}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceCommon/DataHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace PresenceCommon
4 | {
5 | internal static class DataHandler
6 | {
7 | internal static T ByteArrayToStructure(byte[] bytes) where T : struct
8 | {
9 | GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
10 | T data;
11 | try
12 | {
13 | data = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
14 | }
15 | finally
16 | {
17 | handle.Free();
18 | }
19 | return data;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceCommon/PresenceCommon.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceCommon/Types/Title.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 | using System.Text;
3 |
4 | namespace PresenceCommon.Types
5 | {
6 | public class Title
7 | {
8 | public ulong Magic { get; }
9 | public ulong ProgramId { get; }
10 | public string Name { get; }
11 |
12 | [StructLayout(LayoutKind.Sequential, Size = 628)]
13 | private struct TitlePacket
14 | {
15 | [MarshalAs(UnmanagedType.U8)]
16 | public ulong magic;
17 | [MarshalAs(UnmanagedType.U8)]
18 | public ulong programId;
19 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 612)]
20 | public byte[] name;
21 | }
22 |
23 | public Title(byte[] bytes)
24 | {
25 | TitlePacket title = DataHandler.ByteArrayToStructure(bytes);
26 | Magic = title.magic;
27 |
28 | if (title.programId == 0)
29 | {
30 | ProgramId = 0x0100000000001000;
31 | Name = "Home Menu";
32 | }
33 | else
34 | {
35 | ProgramId = title.programId;
36 | Name = Encoding.UTF8.GetString(title.name, 0, title.name.Length).Split('\0')[0];
37 | }
38 | if (title.programId == 0xffaadd23)
39 | {
40 | if (Utils.QuestOverrides.ContainsKey(Name) && Utils.QuestOverrides[Name].CustomName != null)
41 | {
42 | Name = Utils.QuestOverrides[Name].CustomName;
43 | }
44 | }
45 | else
46 | {
47 | if (Utils.SwitchOverrides.ContainsKey($"0{ProgramId:x}") && Utils.SwitchOverrides[$"0{ProgramId:x}"].CustomName != null)
48 | {
49 | Name = Utils.SwitchOverrides[$"0{ProgramId:x}"].CustomName;
50 | }
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/PresenceClient/PresenceCommon/Utils.cs:
--------------------------------------------------------------------------------
1 | using DiscordRPC;
2 | using Newtonsoft.Json;
3 | using PresenceCommon.Types;
4 | using System.Collections.Generic;
5 | using System.Net;
6 | using System.Net.Sockets;
7 |
8 | namespace PresenceCommon
9 | {
10 | public static class Utils
11 | {
12 | public static readonly Dictionary QuestOverrides;
13 | public static readonly Dictionary SwitchOverrides;
14 | static Utils()
15 | {
16 | WebClient client = new WebClient();
17 | string json = client.DownloadString("https://raw.githubusercontent.com/Sun-Research-University/PresenceClient/master/Resource/QuestApplicationOverrides.json");
18 | QuestOverrides = JsonConvert.DeserializeObject>(json);
19 |
20 | json = client.DownloadString("https://raw.githubusercontent.com/Sun-Research-University/PresenceClient/master/Resource/SwitchApplicationOverrides.json");
21 | SwitchOverrides = JsonConvert.DeserializeObject>(json);
22 |
23 | client.Dispose();
24 | }
25 |
26 | public static RichPresence CreateDiscordPresence(Title title, Timestamps time, string largeImageKey = "", string largeImageText = "", string smallImageKey = "", string state = "", bool useProvidedTime = true)
27 | {
28 | RichPresence presence = new RichPresence()
29 | {
30 | State = state
31 | };
32 |
33 | Assets assets = new Assets
34 | {
35 | SmallImageKey = smallImageKey
36 | };
37 |
38 | assets.LargeImageText = title.Name;
39 | if (title.ProgramId != 0xffaadd23)
40 | {
41 | assets.SmallImageText = "SwitchPresence-Rewritten";
42 |
43 | if (!SwitchOverrides.ContainsKey($"0{title.ProgramId:x}"))
44 | {
45 | assets.LargeImageKey = $"0{title.ProgramId:x}";
46 | presence.Details = $"Playing {title.Name}";
47 | }
48 | else
49 | {
50 | OverrideInfo pkgInfo = SwitchOverrides[$"0{title.ProgramId:x}"];
51 | assets.LargeImageKey = pkgInfo.CustomKey ?? $"0{title.ProgramId:x}";
52 |
53 | presence.Details = pkgInfo.CustomPrefix ?? "Playing";
54 | presence.Details += $" {title.Name}";
55 | }
56 | }
57 | else
58 | {
59 | assets.SmallImageText = "QuestPresence";
60 |
61 | if (!QuestOverrides.ContainsKey(title.Name))
62 | {
63 | assets.LargeImageKey = title.Name.ToLower().Replace(" ", "");
64 | presence.Details = $"Playing {title.Name}";
65 | }
66 | else
67 | {
68 | OverrideInfo pkgInfo = QuestOverrides[title.Name];
69 |
70 | assets.LargeImageKey = pkgInfo.CustomKey ?? title.Name.ToLower().Replace(" ", "");
71 |
72 | presence.Details = pkgInfo.CustomPrefix ?? "Playing";
73 | presence.Details += $" {title.Name}";
74 | }
75 | }
76 | if (!string.IsNullOrEmpty(largeImageKey))
77 | assets.LargeImageKey = largeImageKey;
78 |
79 | if (!string.IsNullOrEmpty(largeImageText))
80 | assets.LargeImageText = largeImageText;
81 |
82 | presence.Assets = assets;
83 | if (useProvidedTime)
84 | presence.Timestamps = time;
85 |
86 | return presence;
87 | }
88 |
89 | public static byte[] ReceiveExactly(Socket handler, int length = 628)
90 | {
91 | byte[] buffer = new byte[length];
92 | int receivedLength = 0;
93 | while (receivedLength < length)
94 | {
95 | int nextLength = handler.Receive(buffer, receivedLength, length - receivedLength, SocketFlags.None);
96 | if (nextLength == 0)
97 | {
98 | throw new SocketException();
99 | }
100 | receivedLength += nextLength;
101 | }
102 | return buffer;
103 | }
104 |
105 | public partial class OverrideInfo
106 | {
107 | public string CustomName { set; get; }
108 | public string CustomPrefix { set; get; }
109 | public string CustomKey { set; get; }
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PresenceClient
2 | Set your Discord rich presence using [SwitchPresence-Rewritten](https://github.com/HeadpatServices/SwitchPresence-Rewritten) or [QuestPresence](https://github.com/HeadpatServices/QuestPresence) running this app on your PC.
3 |
4 | # Setup
5 | Simply Create an application at the [Discord Developer Portal](https://discordapp.com/developers/applications/) call your application `Nintendo Switch`, `Oculus Quest` or whatever you would like and then enter your client ID and Device's IP into PresenceClient!
6 |
7 | If you're using QuestPresence, your icon name will be the application name in all lower capitalization with no spaces with the exception of some applications you can take a look [here](https://github.com/HeadpatServices/PresenceClient/blob/master/Resource/QuestApplicationOverrides.json) for those exceptions, you will want to take of a note of the `CustomName` field and format using the above instructions for your icon name. Sometimes an app can have a `CustomKey` field that is filled out, you will want to use this instead of the formatted `CustomName`.
8 |
9 | If you're using SwitchPresence, your icon name will the application title ID, these icons can be dumped from the manager app included in the SwitchPresence release, the dumped icons will be formatted for you to upload directly to your discord developer application.
10 |
11 | Finally to connect you will need your device's IP for QuestPresence this will be on main application page and for SwitchPresence you will have to find it in the connection settings of the switch.
12 |
13 | # Support
14 | If you still need further asstiance you can find us on [Discord](https://link.headpat.services/discord)!
15 |
--------------------------------------------------------------------------------
/Resource/QuestApplicationOverrides.json:
--------------------------------------------------------------------------------
1 | {
2 | "QuestPresence": {
3 | "CustomPrefix": "On the",
4 | "CustomName": "Home Menu",
5 | "CustomKey": null
6 | },
7 | "Oculus App Runtime": {
8 | "CustomPrefix": "On the",
9 | "CustomName": "Home Menu",
10 | "CustomKey": null
11 | },
12 | "Netflix": {
13 | "CustomPrefix": "Watching",
14 | "CustomName": null,
15 | "CustomKey": null
16 | },
17 | "Bigscreen": {
18 | "CustomPrefix": "Watching",
19 | "CustomName": null,
20 | "CustomKey": null
21 | },
22 | "YouTube VR": {
23 | "CustomPrefix": "Watching",
24 | "CustomName": null,
25 | "CustomKey": null
26 | },
27 | "System UI": {
28 | "CustomPrefix": "On the",
29 | "CustomName": "Home Menu",
30 | "CustomKey": null
31 | }
32 | }
--------------------------------------------------------------------------------
/Resource/SwitchApplicationOverrides.json:
--------------------------------------------------------------------------------
1 | {
2 | "01003a400c3da000": {
3 | "CustomPrefix": "Watching",
4 | "CustomName": null,
5 | "CustomKey": null
6 | },
7 | "0100000000001000": {
8 | "CustomPrefix": "In the",
9 | "CustomName": null,
10 | "CustomKey": null
11 | }
12 | }
--------------------------------------------------------------------------------