├── .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 | [![pypresence](https://img.shields.io/badge/using-pypresence-00bb88.svg?style=for-the-badge&logo=discord&logoWidth=20)](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 | } --------------------------------------------------------------------------------