├── .gitattributes
├── .gitignore
├── App.config
├── DESEncryption.cs
├── FriendsGroupForm.Designer.cs
├── FriendsGroupForm.cs
├── FriendsGroupForm.resx
├── FriendsListForm.Designer.cs
├── FriendsListForm.cs
├── FriendsListForm.resx
├── LocalConfig.cs
├── LoginForm.Designer.cs
├── LoginForm.cs
├── LoginForm.resx
├── MainForm.Designer.cs
├── MainForm.cs
├── MainForm.resx
├── MessageForm.Designer.cs
├── MessageForm.cs
├── MessageForm.resx
├── NewInstanceForm.Designer.cs
├── NewInstanceForm.cs
├── NewInstanceForm.resx
├── Properties
├── AssemblyInfo.cs
├── Resources.Designer.cs
├── Resources.resx
├── Settings.Designer.cs
└── Settings.settings
├── README.md
├── Utils.cs
├── VRCEX.cs
├── VRCEX.csproj
├── VRCEX.sln
├── VRChat.cs
├── VRChatRPC.cs
├── VRChatRPC
├── README.txt
└── VRChatRPC.dll
├── packages.config
└── vrchat.ico
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
--------------------------------------------------------------------------------
/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/DESEncryption.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Security.Cryptography;
4 | using System.Text;
5 |
6 | namespace VRCEX
7 | {
8 | public static class DESEncryption
9 | {
10 | private static readonly byte[] m_DESKey = new byte[] { 0x69, 0x00, 0x18, 0x00, 0x58, 0x00, 0x82, 0x00 };
11 |
12 | public static string Encrypt(string plainText, bool randomIV = true)
13 | {
14 | try
15 | {
16 | using (var stream = new MemoryStream())
17 | {
18 | var IV = new byte[8];
19 | if (randomIV)
20 | {
21 | new RNGCryptoServiceProvider().GetBytes(IV);
22 | }
23 | stream.Write(IV, 0, IV.Length);
24 | using (var cryptoStream = new CryptoStream(stream, new DESCryptoServiceProvider().CreateEncryptor(m_DESKey, IV), CryptoStreamMode.Write))
25 | {
26 | var bytes = Encoding.UTF8.GetBytes(plainText);
27 | cryptoStream.Write(bytes, 0, bytes.Length);
28 | cryptoStream.FlushFinalBlock();
29 | return Convert.ToBase64String(stream.ToArray());
30 | }
31 | }
32 | }
33 | catch
34 | {
35 | }
36 | return string.Empty;
37 | }
38 |
39 | public static bool Decrypt(string cipherText, out string plainText)
40 | {
41 | try
42 | {
43 | using (var stream = new MemoryStream(Convert.FromBase64String(cipherText)))
44 | {
45 | var IV = new byte[8];
46 | stream.Read(IV, 0, 8);
47 | using (var cryptoStream = new CryptoStream(stream, new DESCryptoServiceProvider().CreateDecryptor(m_DESKey, IV), CryptoStreamMode.Read))
48 | {
49 | using (var reader = new StreamReader(cryptoStream, Encoding.UTF8))
50 | {
51 | plainText = reader.ReadToEnd();
52 | return true;
53 | }
54 | }
55 | }
56 | }
57 | catch
58 | {
59 | }
60 | plainText = string.Empty;
61 | return false;
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/FriendsGroupForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace VRCEX
2 | {
3 | partial class FriendsGroupForm
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.button_group1 = new System.Windows.Forms.Button();
32 | this.button_group2 = new System.Windows.Forms.Button();
33 | this.button_group3 = new System.Windows.Forms.Button();
34 | this.SuspendLayout();
35 | //
36 | // button_group1
37 | //
38 | this.button_group1.Location = new System.Drawing.Point(12, 13);
39 | this.button_group1.Name = "button_group1";
40 | this.button_group1.Size = new System.Drawing.Size(280, 40);
41 | this.button_group1.TabIndex = 0;
42 | this.button_group1.Text = "Group1";
43 | this.button_group1.UseVisualStyleBackColor = true;
44 | this.button_group1.Click += new System.EventHandler(this.button_group1_Click);
45 | //
46 | // button_group2
47 | //
48 | this.button_group2.Location = new System.Drawing.Point(12, 59);
49 | this.button_group2.Name = "button_group2";
50 | this.button_group2.Size = new System.Drawing.Size(280, 40);
51 | this.button_group2.TabIndex = 1;
52 | this.button_group2.Text = "Group2";
53 | this.button_group2.UseVisualStyleBackColor = true;
54 | this.button_group2.Click += new System.EventHandler(this.button_group2_Click);
55 | //
56 | // button_group3
57 | //
58 | this.button_group3.Location = new System.Drawing.Point(12, 105);
59 | this.button_group3.Name = "button_group3";
60 | this.button_group3.Size = new System.Drawing.Size(280, 40);
61 | this.button_group3.TabIndex = 2;
62 | this.button_group3.Text = "Group3";
63 | this.button_group3.UseVisualStyleBackColor = true;
64 | this.button_group3.Click += new System.EventHandler(this.button_group3_Click);
65 | //
66 | // FriendsGroupForm
67 | //
68 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
69 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
70 | this.ClientSize = new System.Drawing.Size(304, 161);
71 | this.Controls.Add(this.button_group3);
72 | this.Controls.Add(this.button_group2);
73 | this.Controls.Add(this.button_group1);
74 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
75 | this.MaximizeBox = false;
76 | this.Name = "FriendsGroupForm";
77 | this.Text = "Select Group";
78 | this.Load += new System.EventHandler(this.FriendsGroupForm_Load);
79 | this.ResumeLayout(false);
80 |
81 | }
82 |
83 | #endregion
84 |
85 | private System.Windows.Forms.Button button_group1;
86 | private System.Windows.Forms.Button button_group2;
87 | private System.Windows.Forms.Button button_group3;
88 | }
89 | }
--------------------------------------------------------------------------------
/FriendsGroupForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace VRCEX
5 | {
6 | public partial class FriendsGroupForm : Form
7 | {
8 | public FriendsGroupForm()
9 | {
10 | InitializeComponent();
11 | }
12 |
13 | private void FriendsGroupForm_Load(object sender, EventArgs e)
14 | {
15 | if (Tag is string[] a &&
16 | a.Length == 3)
17 | {
18 | button_group1.Text = a[0];
19 | button_group2.Text = a[1];
20 | button_group3.Text = a[2];
21 | }
22 | }
23 |
24 | private void button_group1_Click(object sender, EventArgs e)
25 | {
26 | Tag = "group_0";
27 | Close();
28 | }
29 |
30 | private void button_group2_Click(object sender, EventArgs e)
31 | {
32 | Tag = "group_1";
33 | Close();
34 | }
35 |
36 | private void button_group3_Click(object sender, EventArgs e)
37 | {
38 | Tag = "group_2";
39 | Close();
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/FriendsGroupForm.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 |
--------------------------------------------------------------------------------
/FriendsListForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace VRCEX
2 | {
3 | partial class FriendsListForm
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 | this.listview = new System.Windows.Forms.ListView();
33 | this.timer = new System.Windows.Forms.Timer(this.components);
34 | this.SuspendLayout();
35 | //
36 | // listview
37 | //
38 | this.listview.BorderStyle = System.Windows.Forms.BorderStyle.None;
39 | this.listview.Dock = System.Windows.Forms.DockStyle.Fill;
40 | this.listview.Location = new System.Drawing.Point(0, 0);
41 | this.listview.Margin = new System.Windows.Forms.Padding(0);
42 | this.listview.MultiSelect = false;
43 | this.listview.Name = "listview";
44 | this.listview.ShowItemToolTips = true;
45 | this.listview.Size = new System.Drawing.Size(384, 561);
46 | this.listview.TabIndex = 0;
47 | this.listview.TileSize = new System.Drawing.Size(150, 50);
48 | this.listview.UseCompatibleStateImageBehavior = false;
49 | this.listview.View = System.Windows.Forms.View.Tile;
50 | this.listview.Click += new System.EventHandler(this.listview_Click);
51 | //
52 | // timer
53 | //
54 | this.timer.Enabled = true;
55 | this.timer.Interval = 5000;
56 | this.timer.Tick += new System.EventHandler(this.timer_Tick);
57 | //
58 | // FriendsListForm
59 | //
60 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
61 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
62 | this.ClientSize = new System.Drawing.Size(384, 561);
63 | this.Controls.Add(this.listview);
64 | this.Name = "FriendsListForm";
65 | this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
66 | this.Text = "Friends List";
67 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FriendsSummaryForm_FormClosed);
68 | this.Load += new System.EventHandler(this.FriendsSummaryForm_Load);
69 | this.ResumeLayout(false);
70 |
71 | }
72 |
73 | #endregion
74 |
75 | private System.Windows.Forms.ListView listview;
76 | private System.Windows.Forms.Timer timer;
77 | }
78 | }
--------------------------------------------------------------------------------
/FriendsListForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Windows.Forms;
5 |
6 | namespace VRCEX
7 | {
8 | public partial class FriendsListForm : Form
9 | {
10 | public static FriendsListForm Instance { get; private set; } = null;
11 |
12 | /*private class ListComparer : IComparer
13 | {
14 | public int Compare(object x, object y)
15 | {
16 | return ((ListViewItem)x).Index - ((ListViewItem)y).Index;
17 | }
18 | }*/
19 |
20 | public FriendsListForm()
21 | {
22 | Instance = this;
23 | InitializeComponent();
24 | // listview.ListViewItemSorter = new ListComparer();
25 | // listview.Sorting = SortOrder.Ascending;
26 | listview.SmallImageList = MainForm.Instance.imagelist_listbox;
27 | listview.LargeImageList = MainForm.Instance.imagelist_listbox;
28 | Utils.SetDoubleBuffering(listview);
29 | }
30 |
31 | private void FriendsSummaryForm_Load(object sender, EventArgs e)
32 | {
33 | timer_Tick(null, null);
34 | }
35 |
36 | private void FriendsSummaryForm_FormClosed(object sender, FormClosedEventArgs e)
37 | {
38 | Instance = null;
39 | }
40 |
41 | private void timer_Tick(object sender, EventArgs e)
42 | {
43 | listview.BeginUpdate();
44 | var dic = new Dictionary();
45 | foreach (ListViewItem item in listview.Items)
46 | {
47 | if (item.Tag is ApiUser user)
48 | {
49 | dic[user.id] = item;
50 | }
51 | }
52 | foreach (var user in MainForm.Instance.GetOnlineFriends())
53 | {
54 | var group = listview.Groups[user.location];
55 | if (group != null)
56 | {
57 | group.Header = user.locationInfo;
58 | }
59 | else
60 | {
61 | group = listview.Groups.Add(user.location, user.locationInfo);
62 | }
63 | if (dic.TryGetValue(user.id, out ListViewItem item))
64 | {
65 | dic.Remove(user.id);
66 | if (!group.Equals(item.Group))
67 | {
68 | item.Remove();
69 | }
70 | }
71 | else
72 | {
73 | item = new ListViewItem();
74 | }
75 | item.Tag = user;
76 | item.Text = user.displayName;
77 | item.ToolTipText = $"{user.displayName} ({user.username})\r\n{user.GetTrustLevel()}";
78 | item.ImageKey = user.currentAvatarThumbnailImageUrl;
79 | item.ForeColor = user.GetColor();
80 | var style = FontStyle.Regular;
81 | if (user.location.Contains($"({user.id})"))
82 | {
83 | style |= FontStyle.Bold;
84 | }
85 | /*if (m_Friends.ContainsKey(user.id))
86 | {
87 | style |= FontStyle.Underline;
88 | }*/
89 | if (MainForm.Instance.IsModerated(user.id))
90 | {
91 | style |= FontStyle.Strikeout;
92 | }
93 | if (style != FontStyle.Regular)
94 | {
95 | item.Font = new Font(listview.Font, style);
96 | }
97 | if (item.ListView == null)
98 | {
99 | listview.Items.Add(item);
100 | group.Items.Add(item);
101 | }
102 | }
103 | var groups = new List();
104 | foreach (var pair in dic)
105 | {
106 | var item = pair.Value;
107 | if (item.Group != null &&
108 | item.Group.Items.Count <= 1)
109 | {
110 | groups.Add(item.Group);
111 | }
112 | item.Remove();
113 | }
114 | foreach (var group in groups)
115 | {
116 | listview.Groups.Remove(group);
117 | }
118 | listview.EndUpdate();
119 | }
120 |
121 | private void listview_Click(object sender, EventArgs e)
122 | {
123 | if (listview.Focused &&
124 | listview.SelectedItems.Count > 0 &&
125 | listview.SelectedItems[0].Tag is ApiUser user)
126 | {
127 | MainForm.Instance.ShowUserDetails(user);
128 | }
129 | }
130 | }
131 | }
--------------------------------------------------------------------------------
/FriendsListForm.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 | 17, 17
122 |
123 |
--------------------------------------------------------------------------------
/LocalConfig.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace VRCEX
4 | {
5 | public static class LocalConfig
6 | {
7 | private static Dictionary m_ConfigData = new Dictionary();
8 | private static readonly string CONFIG_FILE_NAME = "config.dat";
9 |
10 | public static void LoadConfig()
11 | {
12 | Utils.Deserialize(CONFIG_FILE_NAME, ref m_ConfigData);
13 | }
14 |
15 | public static void SaveConfig()
16 | {
17 | Utils.Serialize(CONFIG_FILE_NAME, m_ConfigData);
18 | }
19 |
20 | public static bool Remove(string name)
21 | {
22 | return m_ConfigData.Remove(name);
23 | }
24 |
25 | public static string GetString(string name, string defaultValue = "")
26 | {
27 | if (m_ConfigData.TryGetValue(name, out object value) &&
28 | value != null)
29 | {
30 | return value.ToString();
31 | }
32 | return defaultValue;
33 | }
34 |
35 | public static void SetString(string name, string value)
36 | {
37 | m_ConfigData[name] = value;
38 | }
39 |
40 | public static string GetSecureString(string name, string defaultValue = "")
41 | {
42 | var value = GetString(DESEncryption.Encrypt(name, false));
43 | if (!string.IsNullOrEmpty(value) &&
44 | DESEncryption.Decrypt(value.ToString(), out string decrypted))
45 | {
46 | return decrypted;
47 | }
48 | return defaultValue;
49 | }
50 |
51 | public static void SetSecureString(string name, string value)
52 | {
53 | SetString(DESEncryption.Encrypt(name, false), DESEncryption.Encrypt(value));
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/LoginForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace VRCEX
2 | {
3 | partial class LoginForm
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.label_username = new System.Windows.Forms.Label();
32 | this.label_password = new System.Windows.Forms.Label();
33 | this.textbox_username = new System.Windows.Forms.TextBox();
34 | this.textbox_password = new System.Windows.Forms.TextBox();
35 | this.button_login = new System.Windows.Forms.Button();
36 | this.checkbox_username = new System.Windows.Forms.CheckBox();
37 | this.checkbox_password = new System.Windows.Forms.CheckBox();
38 | this.button_login_with_steam = new System.Windows.Forms.Button();
39 | this.SuspendLayout();
40 | //
41 | // label_username
42 | //
43 | this.label_username.AutoSize = true;
44 | this.label_username.Location = new System.Drawing.Point(12, 15);
45 | this.label_username.Name = "label_username";
46 | this.label_username.Size = new System.Drawing.Size(63, 12);
47 | this.label_username.TabIndex = 0;
48 | this.label_username.Text = "Username";
49 | //
50 | // label_password
51 | //
52 | this.label_password.AutoSize = true;
53 | this.label_password.Location = new System.Drawing.Point(12, 42);
54 | this.label_password.Name = "label_password";
55 | this.label_password.Size = new System.Drawing.Size(62, 12);
56 | this.label_password.TabIndex = 3;
57 | this.label_password.Text = "Password";
58 | //
59 | // textbox_username
60 | //
61 | this.textbox_username.Location = new System.Drawing.Point(81, 12);
62 | this.textbox_username.MaxLength = 256;
63 | this.textbox_username.Name = "textbox_username";
64 | this.textbox_username.Size = new System.Drawing.Size(156, 21);
65 | this.textbox_username.TabIndex = 1;
66 | //
67 | // textbox_password
68 | //
69 | this.textbox_password.Location = new System.Drawing.Point(81, 39);
70 | this.textbox_password.MaxLength = 256;
71 | this.textbox_password.Name = "textbox_password";
72 | this.textbox_password.PasswordChar = '*';
73 | this.textbox_password.Size = new System.Drawing.Size(156, 21);
74 | this.textbox_password.TabIndex = 4;
75 | //
76 | // button_login
77 | //
78 | this.button_login.Location = new System.Drawing.Point(183, 66);
79 | this.button_login.Name = "button_login";
80 | this.button_login.Size = new System.Drawing.Size(75, 23);
81 | this.button_login.TabIndex = 7;
82 | this.button_login.Text = "Login";
83 | this.button_login.UseVisualStyleBackColor = true;
84 | this.button_login.Click += new System.EventHandler(this.button_login_Click);
85 | //
86 | // checkbox_username
87 | //
88 | this.checkbox_username.AutoSize = true;
89 | this.checkbox_username.Location = new System.Drawing.Point(243, 15);
90 | this.checkbox_username.Name = "checkbox_username";
91 | this.checkbox_username.Size = new System.Drawing.Size(15, 14);
92 | this.checkbox_username.TabIndex = 2;
93 | this.checkbox_username.UseVisualStyleBackColor = true;
94 | this.checkbox_username.CheckedChanged += new System.EventHandler(this.checkbox_username_CheckedChanged);
95 | //
96 | // checkbox_password
97 | //
98 | this.checkbox_password.AutoSize = true;
99 | this.checkbox_password.Location = new System.Drawing.Point(243, 42);
100 | this.checkbox_password.Name = "checkbox_password";
101 | this.checkbox_password.Size = new System.Drawing.Size(15, 14);
102 | this.checkbox_password.TabIndex = 5;
103 | this.checkbox_password.UseVisualStyleBackColor = true;
104 | //
105 | // button_login_with_steam
106 | //
107 | this.button_login_with_steam.Location = new System.Drawing.Point(57, 66);
108 | this.button_login_with_steam.Name = "button_login_with_steam";
109 | this.button_login_with_steam.Size = new System.Drawing.Size(120, 23);
110 | this.button_login_with_steam.TabIndex = 6;
111 | this.button_login_with_steam.Text = "Login with Steam";
112 | this.button_login_with_steam.UseVisualStyleBackColor = true;
113 | this.button_login_with_steam.Click += new System.EventHandler(this.button_login_with_steam_Click);
114 | //
115 | // LoginForm
116 | //
117 | this.AcceptButton = this.button_login;
118 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
119 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
120 | this.ClientSize = new System.Drawing.Size(269, 101);
121 | this.Controls.Add(this.checkbox_password);
122 | this.Controls.Add(this.checkbox_username);
123 | this.Controls.Add(this.button_login_with_steam);
124 | this.Controls.Add(this.button_login);
125 | this.Controls.Add(this.textbox_password);
126 | this.Controls.Add(this.textbox_username);
127 | this.Controls.Add(this.label_password);
128 | this.Controls.Add(this.label_username);
129 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
130 | this.KeyPreview = true;
131 | this.MaximizeBox = false;
132 | this.Name = "LoginForm";
133 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
134 | this.Text = "VRChat Login";
135 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.LoginForm_FormClosed);
136 | this.Load += new System.EventHandler(this.LoginForm_Load);
137 | this.ResumeLayout(false);
138 | this.PerformLayout();
139 |
140 | }
141 |
142 | #endregion
143 |
144 | private System.Windows.Forms.Label label_username;
145 | private System.Windows.Forms.Label label_password;
146 | private System.Windows.Forms.TextBox textbox_username;
147 | private System.Windows.Forms.TextBox textbox_password;
148 | private System.Windows.Forms.Button button_login;
149 | private System.Windows.Forms.CheckBox checkbox_username;
150 | private System.Windows.Forms.CheckBox checkbox_password;
151 | private System.Windows.Forms.Button button_login_with_steam;
152 | }
153 | }
--------------------------------------------------------------------------------
/LoginForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Windows.Forms;
4 |
5 | namespace VRCEX
6 | {
7 | public partial class LoginForm : Form
8 | {
9 | public static LoginForm Instance { get; private set; } = null;
10 |
11 | private void LoadAuth()
12 | {
13 | var auth = LocalConfig.GetSecureString("Auth");
14 | if (!string.IsNullOrEmpty(auth))
15 | {
16 | var a = auth.Split(new[] { '|' }, 2);
17 | textbox_username.Text = a[0];
18 | checkbox_username.Checked = true;
19 | if (a.Length == 2)
20 | {
21 | textbox_password.Text = a[1];
22 | checkbox_password.Enabled = true;
23 | checkbox_password.Checked = true;
24 | }
25 | }
26 | else
27 | {
28 | checkbox_username.Checked = false;
29 | checkbox_password.Checked = false;
30 | checkbox_password.Enabled = false;
31 | }
32 | }
33 |
34 | private void SaveAuth()
35 | {
36 | var auth = string.Empty;
37 | if (checkbox_username.Checked)
38 | {
39 | auth += textbox_username.Text;
40 | if (checkbox_password.Checked)
41 | {
42 | auth += "|" + textbox_password.Text;
43 | }
44 | }
45 | LocalConfig.SetSecureString("Auth", auth);
46 | }
47 |
48 | public LoginForm()
49 | {
50 | Instance = this;
51 | InitializeComponent();
52 | }
53 |
54 | private void LoginForm_Load(object sender, EventArgs e)
55 | {
56 | LoadAuth();
57 | if (MainForm.LastLoginSuccess)
58 | {
59 | button_login_Click(null, null); // FIXME: steam login
60 | }
61 | }
62 |
63 | private void LoginForm_FormClosed(object sender, FormClosedEventArgs e)
64 | {
65 | SaveAuth();
66 | Instance = null;
67 | }
68 |
69 | private void button_login_Click(object sender, EventArgs e)
70 | {
71 | if (Enabled)
72 | {
73 | var username = textbox_username.Text;
74 | var password = textbox_password.Text;
75 | if (!(string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)))
76 | {
77 | Enabled = false;
78 | VRCApi.Login(username, password);
79 | }
80 | }
81 | }
82 |
83 | private void button_login_with_steam_Click(object sender, EventArgs e)
84 | {
85 | if (Enabled)
86 | {
87 | if (VRChatRPC.Update())
88 | {
89 | Enabled = false;
90 | VRCApi.ThridPartyLogin("steam", new Dictionary
91 | {
92 | ["steamTicket"] = VRChatRPC.GetAuthSessionTicket()
93 | });
94 | }
95 | else
96 | {
97 | MessageBox.Show("Login via Steam failed, It only works when VRChat is running.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
98 | }
99 | }
100 | }
101 |
102 | private void checkbox_username_CheckedChanged(object sender, EventArgs e)
103 | {
104 | if (!checkbox_username.Checked)
105 | {
106 | checkbox_password.Checked = false;
107 | }
108 | checkbox_password.Enabled = checkbox_username.Checked;
109 | }
110 |
111 | public void Reset()
112 | {
113 | Enabled = true;
114 | }
115 | }
116 | }
--------------------------------------------------------------------------------
/LoginForm.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 |
--------------------------------------------------------------------------------
/MainForm.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 | 17, 17
122 |
123 |
124 | 142, 17
125 |
126 |
127 | 272, 17
128 |
129 |
130 | To Disclaimers:
131 |
132 | VRCEX is an assistant application for provide information about manage friendship. this application uses unofficial VRChat API (VRCSDK). Use at your own risk.
133 | VRChat is trademark of VRChat Inc. VRChat © VRChat Inc. VRCEX is not associated with VRChat Inc.
134 |
135 |
--------------------------------------------------------------------------------
/MessageForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace VRCEX
2 | {
3 | partial class MessageForm
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.button_send = new System.Windows.Forms.Button();
32 | this.textbox_user = new System.Windows.Forms.TextBox();
33 | this.label1 = new System.Windows.Forms.Label();
34 | this.textbox_message = new System.Windows.Forms.TextBox();
35 | this.label3 = new System.Windows.Forms.Label();
36 | this.SuspendLayout();
37 | //
38 | // button_send
39 | //
40 | this.button_send.Location = new System.Drawing.Point(12, 124);
41 | this.button_send.Name = "button_send";
42 | this.button_send.Size = new System.Drawing.Size(311, 25);
43 | this.button_send.TabIndex = 2;
44 | this.button_send.Text = "Send";
45 | this.button_send.UseVisualStyleBackColor = true;
46 | this.button_send.Click += new System.EventHandler(this.button_send_Click);
47 | //
48 | // textbox_user
49 | //
50 | this.textbox_user.BackColor = System.Drawing.SystemColors.Window;
51 | this.textbox_user.Location = new System.Drawing.Point(12, 24);
52 | this.textbox_user.Name = "textbox_user";
53 | this.textbox_user.ReadOnly = true;
54 | this.textbox_user.Size = new System.Drawing.Size(310, 21);
55 | this.textbox_user.TabIndex = 4;
56 | //
57 | // label1
58 | //
59 | this.label1.AutoSize = true;
60 | this.label1.Location = new System.Drawing.Point(10, 9);
61 | this.label1.Name = "label1";
62 | this.label1.Size = new System.Drawing.Size(54, 12);
63 | this.label1.TabIndex = 3;
64 | this.label1.Text = "Receiver";
65 | //
66 | // textbox_message
67 | //
68 | this.textbox_message.BackColor = System.Drawing.SystemColors.Window;
69 | this.textbox_message.Location = new System.Drawing.Point(12, 68);
70 | this.textbox_message.Multiline = true;
71 | this.textbox_message.Name = "textbox_message";
72 | this.textbox_message.Size = new System.Drawing.Size(310, 50);
73 | this.textbox_message.TabIndex = 1;
74 | //
75 | // label3
76 | //
77 | this.label3.AutoSize = true;
78 | this.label3.Location = new System.Drawing.Point(10, 53);
79 | this.label3.Name = "label3";
80 | this.label3.Size = new System.Drawing.Size(58, 12);
81 | this.label3.TabIndex = 0;
82 | this.label3.Text = "Message";
83 | //
84 | // MessageForm
85 | //
86 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
87 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
88 | this.ClientSize = new System.Drawing.Size(334, 161);
89 | this.Controls.Add(this.label3);
90 | this.Controls.Add(this.textbox_message);
91 | this.Controls.Add(this.label1);
92 | this.Controls.Add(this.textbox_user);
93 | this.Controls.Add(this.button_send);
94 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
95 | this.KeyPreview = true;
96 | this.MaximizeBox = false;
97 | this.MinimizeBox = false;
98 | this.Name = "MessageForm";
99 | this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
100 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
101 | this.Text = "Send Message";
102 | this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.MessageForm_KeyPress);
103 | this.ResumeLayout(false);
104 | this.PerformLayout();
105 |
106 | }
107 |
108 | #endregion
109 | private System.Windows.Forms.Button button_send;
110 | private System.Windows.Forms.TextBox textbox_user;
111 | private System.Windows.Forms.Label label1;
112 | private System.Windows.Forms.TextBox textbox_message;
113 | private System.Windows.Forms.Label label3;
114 | }
115 | }
--------------------------------------------------------------------------------
/MessageForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace VRCEX
5 | {
6 | public partial class MessageForm : Form
7 | {
8 | private string m_User = string.Empty;
9 |
10 | public MessageForm()
11 | {
12 | InitializeComponent();
13 | }
14 |
15 | public void Run(string id, string name)
16 | {
17 | m_User = id;
18 | textbox_user.Text = name;
19 | ShowDialog(MainForm.Instance);
20 | }
21 |
22 | private void MessageForm_KeyPress(object sender, KeyPressEventArgs e)
23 | {
24 | if (e.KeyChar == (char)Keys.Escape)
25 | {
26 | e.Handled = true;
27 | Close();
28 | }
29 | }
30 |
31 | private void button_send_Click(object sender, EventArgs e)
32 | {
33 | ApiUser.SendMessage(m_User, textbox_message.Text);
34 | Close();
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/MessageForm.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 |
--------------------------------------------------------------------------------
/NewInstanceForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace VRCEX
2 | {
3 | partial class NewInstanceForm
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.radiobutton_public = new System.Windows.Forms.RadioButton();
32 | this.radiobutton_friendsonly = new System.Windows.Forms.RadioButton();
33 | this.radiobutton_friendsofguests = new System.Windows.Forms.RadioButton();
34 | this.radiobutton_inviteplus = new System.Windows.Forms.RadioButton();
35 | this.radiobutton_inviteonly = new System.Windows.Forms.RadioButton();
36 | this.textbox_world_id = new System.Windows.Forms.TextBox();
37 | this.button_launch = new System.Windows.Forms.Button();
38 | this.textbox_vrchat_link = new System.Windows.Forms.TextBox();
39 | this.label1 = new System.Windows.Forms.Label();
40 | this.textbox_web_link = new System.Windows.Forms.TextBox();
41 | this.label3 = new System.Windows.Forms.Label();
42 | this.SuspendLayout();
43 | //
44 | // radiobutton_public
45 | //
46 | this.radiobutton_public.AutoSize = true;
47 | this.radiobutton_public.Location = new System.Drawing.Point(12, 12);
48 | this.radiobutton_public.Name = "radiobutton_public";
49 | this.radiobutton_public.Size = new System.Drawing.Size(170, 16);
50 | this.radiobutton_public.TabIndex = 0;
51 | this.radiobutton_public.Text = "Public — Anyone can join";
52 | this.radiobutton_public.UseVisualStyleBackColor = true;
53 | this.radiobutton_public.CheckedChanged += new System.EventHandler(this.radiobutton_CheckedChanged);
54 | //
55 | // radiobutton_friendsonly
56 | //
57 | this.radiobutton_friendsonly.AutoSize = true;
58 | this.radiobutton_friendsonly.Location = new System.Drawing.Point(12, 56);
59 | this.radiobutton_friendsonly.Name = "radiobutton_friendsonly";
60 | this.radiobutton_friendsonly.Size = new System.Drawing.Size(235, 16);
61 | this.radiobutton_friendsonly.TabIndex = 2;
62 | this.radiobutton_friendsonly.TabStop = true;
63 | this.radiobutton_friendsonly.Text = "Friends — Only your friends may join";
64 | this.radiobutton_friendsonly.UseVisualStyleBackColor = true;
65 | this.radiobutton_friendsonly.CheckedChanged += new System.EventHandler(this.radiobutton_CheckedChanged);
66 | //
67 | // radiobutton_friendsofguests
68 | //
69 | this.radiobutton_friendsofguests.AutoSize = true;
70 | this.radiobutton_friendsofguests.Location = new System.Drawing.Point(12, 34);
71 | this.radiobutton_friendsofguests.Name = "radiobutton_friendsofguests";
72 | this.radiobutton_friendsofguests.Size = new System.Drawing.Size(342, 16);
73 | this.radiobutton_friendsofguests.TabIndex = 1;
74 | this.radiobutton_friendsofguests.TabStop = true;
75 | this.radiobutton_friendsofguests.Text = "Friends+ — Any friend of a user in the instance may join";
76 | this.radiobutton_friendsofguests.UseVisualStyleBackColor = true;
77 | this.radiobutton_friendsofguests.CheckedChanged += new System.EventHandler(this.radiobutton_CheckedChanged);
78 | //
79 | // radiobutton_inviteplus
80 | //
81 | this.radiobutton_inviteplus.AutoSize = true;
82 | this.radiobutton_inviteplus.Location = new System.Drawing.Point(12, 78);
83 | this.radiobutton_inviteplus.Name = "radiobutton_inviteplus";
84 | this.radiobutton_inviteplus.Size = new System.Drawing.Size(366, 16);
85 | this.radiobutton_inviteplus.TabIndex = 3;
86 | this.radiobutton_inviteplus.TabStop = true;
87 | this.radiobutton_inviteplus.Text = "Invite+ — You can invite others. Joiners can accept requests";
88 | this.radiobutton_inviteplus.UseVisualStyleBackColor = true;
89 | this.radiobutton_inviteplus.CheckedChanged += new System.EventHandler(this.radiobutton_CheckedChanged);
90 | //
91 | // radiobutton_inviteonly
92 | //
93 | this.radiobutton_inviteonly.AutoSize = true;
94 | this.radiobutton_inviteonly.Location = new System.Drawing.Point(12, 100);
95 | this.radiobutton_inviteonly.Name = "radiobutton_inviteonly";
96 | this.radiobutton_inviteonly.Size = new System.Drawing.Size(370, 16);
97 | this.radiobutton_inviteonly.TabIndex = 4;
98 | this.radiobutton_inviteonly.TabStop = true;
99 | this.radiobutton_inviteonly.Text = "Invite — You can invite others. Only you can accept requests";
100 | this.radiobutton_inviteonly.UseVisualStyleBackColor = true;
101 | this.radiobutton_inviteonly.CheckedChanged += new System.EventHandler(this.radiobutton_CheckedChanged);
102 | //
103 | // textbox_world_id
104 | //
105 | this.textbox_world_id.BackColor = System.Drawing.SystemColors.Window;
106 | this.textbox_world_id.Location = new System.Drawing.Point(12, 144);
107 | this.textbox_world_id.Name = "textbox_world_id";
108 | this.textbox_world_id.Size = new System.Drawing.Size(460, 21);
109 | this.textbox_world_id.TabIndex = 6;
110 | this.textbox_world_id.TextChanged += new System.EventHandler(this.textbox_world_id_TextChanged);
111 | //
112 | // button_launch
113 | //
114 | this.button_launch.Location = new System.Drawing.Point(12, 247);
115 | this.button_launch.Name = "button_launch";
116 | this.button_launch.Size = new System.Drawing.Size(461, 25);
117 | this.button_launch.TabIndex = 10;
118 | this.button_launch.Text = "Launch";
119 | this.button_launch.UseVisualStyleBackColor = true;
120 | this.button_launch.Click += new System.EventHandler(this.button_launch_Click);
121 | //
122 | // textbox_vrchat_link
123 | //
124 | this.textbox_vrchat_link.BackColor = System.Drawing.SystemColors.Window;
125 | this.textbox_vrchat_link.Location = new System.Drawing.Point(12, 171);
126 | this.textbox_vrchat_link.Name = "textbox_vrchat_link";
127 | this.textbox_vrchat_link.ReadOnly = true;
128 | this.textbox_vrchat_link.Size = new System.Drawing.Size(460, 21);
129 | this.textbox_vrchat_link.TabIndex = 7;
130 | this.textbox_vrchat_link.Click += new System.EventHandler(this.textbox_Click);
131 | //
132 | // label1
133 | //
134 | this.label1.AutoSize = true;
135 | this.label1.Location = new System.Drawing.Point(12, 129);
136 | this.label1.Name = "label1";
137 | this.label1.Size = new System.Drawing.Size(51, 12);
138 | this.label1.TabIndex = 5;
139 | this.label1.Text = "World ID";
140 | //
141 | // textbox_web_link
142 | //
143 | this.textbox_web_link.BackColor = System.Drawing.SystemColors.Window;
144 | this.textbox_web_link.Location = new System.Drawing.Point(12, 215);
145 | this.textbox_web_link.Name = "textbox_web_link";
146 | this.textbox_web_link.ReadOnly = true;
147 | this.textbox_web_link.Size = new System.Drawing.Size(460, 21);
148 | this.textbox_web_link.TabIndex = 9;
149 | this.textbox_web_link.Click += new System.EventHandler(this.textbox_Click);
150 | //
151 | // label3
152 | //
153 | this.label3.AutoSize = true;
154 | this.label3.Location = new System.Drawing.Point(12, 200);
155 | this.label3.Name = "label3";
156 | this.label3.Size = new System.Drawing.Size(65, 12);
157 | this.label3.TabIndex = 8;
158 | this.label3.Text = "Share Link";
159 | //
160 | // NewInstanceForm
161 | //
162 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
163 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
164 | this.ClientSize = new System.Drawing.Size(484, 281);
165 | this.Controls.Add(this.label3);
166 | this.Controls.Add(this.textbox_web_link);
167 | this.Controls.Add(this.label1);
168 | this.Controls.Add(this.textbox_vrchat_link);
169 | this.Controls.Add(this.button_launch);
170 | this.Controls.Add(this.textbox_world_id);
171 | this.Controls.Add(this.radiobutton_inviteonly);
172 | this.Controls.Add(this.radiobutton_inviteplus);
173 | this.Controls.Add(this.radiobutton_friendsofguests);
174 | this.Controls.Add(this.radiobutton_friendsonly);
175 | this.Controls.Add(this.radiobutton_public);
176 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
177 | this.KeyPreview = true;
178 | this.MaximizeBox = false;
179 | this.MinimizeBox = false;
180 | this.Name = "NewInstanceForm";
181 | this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
182 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
183 | this.Text = "New Instance";
184 | this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.NewWorldInstanceForm_KeyPress);
185 | this.ResumeLayout(false);
186 | this.PerformLayout();
187 |
188 | }
189 |
190 | #endregion
191 |
192 | private System.Windows.Forms.RadioButton radiobutton_public;
193 | private System.Windows.Forms.RadioButton radiobutton_friendsonly;
194 | private System.Windows.Forms.RadioButton radiobutton_friendsofguests;
195 | private System.Windows.Forms.RadioButton radiobutton_inviteplus;
196 | private System.Windows.Forms.RadioButton radiobutton_inviteonly;
197 | private System.Windows.Forms.TextBox textbox_world_id;
198 | private System.Windows.Forms.Button button_launch;
199 | private System.Windows.Forms.TextBox textbox_vrchat_link;
200 | private System.Windows.Forms.Label label1;
201 | private System.Windows.Forms.TextBox textbox_web_link;
202 | private System.Windows.Forms.Label label3;
203 | }
204 | }
--------------------------------------------------------------------------------
/NewInstanceForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using System.Windows.Forms;
4 |
5 | namespace VRCEX
6 | {
7 | public partial class NewInstanceForm : Form
8 | {
9 | private Random m_Random = new Random();
10 | private string m_User = string.Empty;
11 | private string m_Instance = string.Empty;
12 |
13 | public NewInstanceForm()
14 | {
15 | InitializeComponent();
16 | }
17 |
18 | public void Run(string user, string world)
19 | {
20 | m_User = user;
21 | textbox_world_id.Text = world;
22 | radiobutton_public.Checked = true;
23 | ShowDialog(MainForm.Instance);
24 | }
25 |
26 | private void NewWorldInstanceForm_KeyPress(object sender, KeyPressEventArgs e)
27 | {
28 | if (e.KeyChar == (char)Keys.Escape)
29 | {
30 | e.Handled = true;
31 | Close();
32 | }
33 | }
34 |
35 | private void radiobutton_CheckedChanged(object sender, EventArgs e)
36 | {
37 | var b = new StringBuilder();
38 | b.Append(m_Random.Next(1, 99999));
39 | if (!radiobutton_public.Checked)
40 | {
41 | var type = "private";
42 | if (radiobutton_friendsofguests.Checked)
43 | {
44 | type = "hidden";
45 | }
46 | else if (radiobutton_friendsonly.Checked)
47 | {
48 | type = "friends";
49 | }
50 | b.AppendFormat("~{0}({1})~nonce({2})", type, m_User, Guid.NewGuid());
51 | if (radiobutton_inviteplus.Checked)
52 | {
53 | b.Append("~canRequestInvite");
54 | }
55 | }
56 | m_Instance = b.ToString();
57 | textbox_world_id_TextChanged(sender, e);
58 | }
59 |
60 | private void textbox_world_id_TextChanged(object sender, EventArgs e)
61 | {
62 | var world = textbox_world_id.Text.Trim();
63 | if (!string.IsNullOrEmpty(world))
64 | {
65 | textbox_vrchat_link.Text = "vrchat://launch?id=" + world + ":" + m_Instance;
66 | textbox_web_link.Text = "https://www.vrchat.net/home/launch?worldId=" + world + "&instanceId=" + m_Instance;
67 | }
68 | }
69 |
70 | private void textbox_Click(object sender, EventArgs e)
71 | {
72 | if (sender is TextBox textbox)
73 | {
74 | textbox.SelectAll();
75 | }
76 | }
77 |
78 | private void button_launch_Click(object sender, EventArgs e)
79 | {
80 | var s = textbox_vrchat_link.Text;
81 | if (!string.IsNullOrEmpty(s) &&
82 | MessageBox.Show(this, s, "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
83 | {
84 | System.Diagnostics.Process.Start(s);
85 | }
86 | }
87 | }
88 | }
--------------------------------------------------------------------------------
/NewInstanceForm.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 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
6 | // 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
7 | // 이러한 특성 값을 변경하세요.
8 | [assembly: AssemblyTitle("VRCEX")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("VRCEX")]
13 | [assembly: AssemblyCopyright("")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에
18 | // 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면
19 | // 해당 형식에 대해 ComVisible 특성을 true로 설정하세요.
20 | [assembly: ComVisible(false)]
21 |
22 | // 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
23 | [assembly: Guid("2c30787a-a8d5-42b1-9da2-742d8253210c")]
24 |
25 | // 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
26 | //
27 | // 주 버전
28 | // 부 버전
29 | // 빌드 번호
30 | // 수정 버전
31 | //
32 | // 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호가 자동으로
33 | // 지정되도록 할 수 있습니다.
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("0.0.0.0")]
36 | [assembly: AssemblyFileVersion("0.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // 이 코드는 도구를 사용하여 생성되었습니다.
4 | // 런타임 버전:4.0.30319.42000
5 | //
6 | // 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
7 | // 이러한 변경 내용이 손실됩니다.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace VRCEX.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// 지역화된 문자열 등을 찾기 위한 강력한 형식의 리소스 클래스입니다.
17 | ///
18 | // 이 클래스는 ResGen 또는 Visual Studio와 같은 도구를 통해 StronglyTypedResourceBuilder
19 | // 클래스에서 자동으로 생성되었습니다.
20 | // 멤버를 추가하거나 제거하려면 .ResX 파일을 편집한 다음 /str 옵션을 사용하여 ResGen을
21 | // 다시 실행하거나 VS 프로젝트를 다시 빌드하십시오.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.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 | /// 이 클래스에서 사용하는 캐시된 ResourceManager 인스턴스를 반환합니다.
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("VRCEX.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// 이 강력한 형식의 리소스 클래스를 사용하여 모든 리소스 조회에 대해 현재 스레드의 CurrentUICulture 속성을
51 | /// 재정의합니다.
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 |
--------------------------------------------------------------------------------
/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 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // 이 코드는 도구를 사용하여 생성되었습니다.
4 | // 런타임 버전:4.0.30319.42000
5 | //
6 | // 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
7 | // 이러한 변경 내용이 손실됩니다.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace VRCEX.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.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 |
--------------------------------------------------------------------------------
/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DEPRECATED
2 |
3 | This repository is moved to [pypy-vrc/VRCX](https://github.com/pypy-vrc/VRCX).
4 |
5 | VRCEX
6 | =
7 | 
8 | 
9 | 
10 | 
11 | 
12 | 
13 | 
14 | 
15 | 
16 | 
17 | 
18 | 
19 |
--------------------------------------------------------------------------------
/Utils.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Reflection;
3 | using System.Runtime.Serialization.Formatters.Binary;
4 | using System.Windows.Forms;
5 |
6 | namespace VRCEX
7 | {
8 | public static class Utils
9 | {
10 | public static void SetDoubleBuffering(Control control)
11 | {
12 | var type = control.GetType();
13 | type.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(control, true, null);
14 | type.GetMethod("SetStyle", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(control, new object[] { ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw, true });
15 | }
16 |
17 | public static void Serialize(string path, object obj)
18 | {
19 | try
20 | {
21 | using (var file = File.Open(path, FileMode.Create, FileAccess.Write))
22 | {
23 | new BinaryFormatter().Serialize(file, obj);
24 | }
25 | }
26 | catch
27 | {
28 | }
29 | }
30 |
31 | public static bool Deserialize(string path, ref T obj)
32 | {
33 | try
34 | {
35 | using (var file = File.Open(path, FileMode.Open, FileAccess.Read))
36 | {
37 | obj = (T)new BinaryFormatter().Deserialize(file);
38 | return true;
39 | }
40 | }
41 | catch
42 | {
43 | }
44 | return false;
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/VRCEX.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace VRCEX
5 | {
6 | public static class VRCEX
7 | {
8 | public static readonly string APP = "VRCEX v0.09a";
9 |
10 | [STAThread]
11 | public static void Main()
12 | {
13 | Application.EnableVisualStyles();
14 | Application.SetCompatibleTextRenderingDefault(false);
15 | LocalConfig.LoadConfig();
16 | VRCApi.LoadCookie();
17 | Application.Run(new MainForm());
18 | VRCApi.SaveCookie();
19 | LocalConfig.SaveConfig();
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/VRCEX.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {2C30787A-A8D5-42B1-9DA2-742D8253210C}
8 | WinExe
9 | VRCEX
10 | VRCEX
11 | v4.5.2
12 | 512
13 |
14 |
15 |
16 | x64
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | x64
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 | vrchat.ico
36 |
37 |
38 | true
39 |
40 |
41 |
42 | packages\Jil.2.17.0\lib\net45\Jil.dll
43 |
44 |
45 | packages\Sigil.4.7.0\lib\net45\Sigil.dll
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | Form
64 |
65 |
66 | FriendsGroupForm.cs
67 |
68 |
69 | Form
70 |
71 |
72 | FriendsListForm.cs
73 |
74 |
75 | Form
76 |
77 |
78 | LoginForm.cs
79 |
80 |
81 | Form
82 |
83 |
84 | MainForm.cs
85 |
86 |
87 | Form
88 |
89 |
90 | MessageForm.cs
91 |
92 |
93 | Form
94 |
95 |
96 | NewInstanceForm.cs
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | FriendsGroupForm.cs
106 |
107 |
108 | FriendsListForm.cs
109 |
110 |
111 | LoginForm.cs
112 |
113 |
114 | MainForm.cs
115 |
116 |
117 | MessageForm.cs
118 |
119 |
120 | NewInstanceForm.cs
121 |
122 |
123 | ResXFileCodeGenerator
124 | Resources.Designer.cs
125 | Designer
126 |
127 |
128 | True
129 | Resources.resx
130 | True
131 |
132 |
133 |
134 | SettingsSingleFileGenerator
135 | Settings.Designer.cs
136 |
137 |
138 | True
139 | Settings.settings
140 | True
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/VRCEX.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27703.2042
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VRCEX", "VRCEX.csproj", "{2C30787A-A8D5-42B1-9DA2-742D8253210C}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {2C30787A-A8D5-42B1-9DA2-742D8253210C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {2C30787A-A8D5-42B1-9DA2-742D8253210C}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {2C30787A-A8D5-42B1-9DA2-742D8253210C}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {2C30787A-A8D5-42B1-9DA2-742D8253210C}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {491D928C-F2FB-4F08-9C83-754E67CDF98A}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/VRChat.cs:
--------------------------------------------------------------------------------
1 | using Jil;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Drawing;
5 | using System.IO;
6 | using System.Net;
7 | using System.Text;
8 |
9 | namespace VRCEX
10 | {
11 | public enum ApiMethod
12 | {
13 | GET,
14 | POST,
15 | PUT,
16 | DELETE
17 | }
18 |
19 | public class ApiResult
20 | {
21 | public string message;
22 | public int status_code;
23 | }
24 |
25 | public class ApiResponse
26 | {
27 | public ApiResult error;
28 | public ApiResult success;
29 | }
30 |
31 | //
32 | // VRChat
33 | //
34 |
35 | // 2018-11-07
36 | // https://vrchat.net/api/1/youtube
37 | // https://api.vrchat.cloud/api/1/youtube
38 | // 138.197.0.101, 138.197.1.206, 138.197.99.115
39 | // 159.89.32.95, 159.203.172.3, 159.203.173.149
40 |
41 | // 2019-04-17
42 | // https://pipeline.vrchat.cloud
43 | // 138.197.0.101, 138.197.1.206, 138.197.99.115
44 | // 159.89.32.95, 159.203.163.197, 159.203.172.3, 159.203.173.149, 159.65.169.82
45 |
46 | // To change display name:
47 | // https://vrchat.net/api/1/auth/exists?apiKey=JlE5Jldo5Jibnk5O5hTx6XVqsJu4WJ26&username=&displayName=dildo
48 | // $.ajax({ url: window.endpoint + '/api/1/users/usr_75efc1e5-700f-447b-9beb-11acd9c3f2a9', type: 'PUT', data: {displayName: 'SmartHE'}, success: console.log, error: console.log });
49 | // $.ajax({ url: window.endpoint + '/api/1/user/usr_4f76a584-9d4b-46f6-8209-8305eb683661/message', type: 'POST', data: {type: 'message', message: 'test'}, success: console.log, error: console.log });
50 |
51 | public static class VRCApi
52 | {
53 | private static readonly string API_URL = "https://api.vrchat.cloud/api/1/";
54 | private static readonly string COOKIE_FILE_NAME = "cookie.dat";
55 | private static string m_ApiKey = string.Empty;
56 | private static CookieContainer m_CookieContainer = new CookieContainer();
57 |
58 | static VRCApi()
59 | {
60 | RemoteConfig.FetchConfig((config) =>
61 | {
62 | m_ApiKey = config.apiKey;
63 | });
64 | }
65 |
66 | public static void ClearCookie()
67 | {
68 | m_CookieContainer = new CookieContainer();
69 | }
70 |
71 | public static void LoadCookie()
72 | {
73 | Utils.Deserialize(COOKIE_FILE_NAME, ref m_CookieContainer);
74 | }
75 |
76 | public static void SaveCookie()
77 | {
78 | Utils.Serialize(COOKIE_FILE_NAME, m_CookieContainer);
79 | }
80 |
81 | private static void HandleJson(HttpWebResponse response, Action callback)
82 | {
83 | using (var stream = response.GetResponseStream())
84 | {
85 | using (var reader = new StreamReader(stream))
86 | {
87 | callback.Invoke(JSON.Deserialize(reader));
88 | }
89 | }
90 | }
91 |
92 | public static async void Request(Action callback, string endpoint, ApiMethod method = ApiMethod.GET, Dictionary data = null, Action setup = null)
93 | {
94 | try
95 | {
96 | var uri = new UriBuilder(API_URL + endpoint);
97 | var query = new StringBuilder(uri.Query);
98 | if (query.Length > 0)
99 | {
100 | query.Remove(0, 1);
101 | }
102 | if (method == ApiMethod.GET &&
103 | data != null)
104 | {
105 | foreach (var pair in data)
106 | {
107 | if (query.Length > 0)
108 | {
109 | query.Append('&');
110 | }
111 | query.Append(pair.Key);
112 | query.Append('=');
113 | if (pair.Value != null)
114 | {
115 | query.Append(Uri.EscapeDataString(pair.Value.ToString()));
116 | }
117 | }
118 | }
119 | if (!string.IsNullOrEmpty(m_ApiKey) &&
120 | !"config".Equals(uri.Path))
121 | {
122 | query.Insert(0, "apiKey=" + m_ApiKey + (query.Length > 0 ? "&" : string.Empty));
123 | }
124 | uri.Query = query.ToString();
125 | var request = WebRequest.CreateHttp(uri.Uri);
126 | request.CookieContainer = m_CookieContainer;
127 | request.KeepAlive = true;
128 | request.Method = method.ToString();
129 | request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko";
130 | if (setup != null)
131 | {
132 | setup.Invoke(request);
133 | }
134 | if (method != ApiMethod.GET &&
135 | data != null)
136 | {
137 | request.ContentType = "application/json;charset=utf-8";
138 | using (var stream = await request.GetRequestStreamAsync())
139 | {
140 | using (var writer = new StreamWriter(stream))
141 | {
142 | JSON.SerializeDynamic(data, writer);
143 | }
144 | }
145 | }
146 | using (var response = await request.GetResponseAsync() as HttpWebResponse)
147 | {
148 | HandleJson(response, callback);
149 | }
150 | }
151 | catch (WebException w)
152 | {
153 | try
154 | {
155 | using (var response = w.Response as HttpWebResponse)
156 | {
157 | if (response.ContentType != null && response.ContentType.IndexOf("json", StringComparison.OrdinalIgnoreCase) != -1)
158 | {
159 | HandleJson(response, MainForm.Instance.OnResponse);
160 | }
161 | else
162 | {
163 | MainForm.Instance.ShowMessage(endpoint + ": " + w.Message);
164 | }
165 | }
166 | }
167 | catch (Exception x)
168 | {
169 | MainForm.Instance.ShowMessage(endpoint + ": " + x.Message);
170 | }
171 | }
172 | catch (Exception x)
173 | {
174 | MainForm.Instance.ShowMessage(endpoint + ": " + x.Message);
175 | }
176 | }
177 |
178 | public static void Login(string username, string password)
179 | {
180 | ClearCookie();
181 | Request(MainForm.Instance.OnLogin, "auth/user", ApiMethod.GET, null, (request) =>
182 | {
183 | request.Headers["Authorization"] = $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"))}";
184 | });
185 | }
186 |
187 | public static void ThridPartyLogin(string endpoint, Dictionary param)
188 | {
189 | ClearCookie();
190 | Request(MainForm.Instance.OnLogin, $"auth/{endpoint}", ApiMethod.POST, param);
191 | }
192 |
193 | public static async void FetchVisits()
194 | {
195 | try
196 | {
197 | var request = WebRequest.CreateHttp(API_URL + "visits");
198 | request.KeepAlive = true;
199 | request.Method = "GET";
200 | using (var response = await request.GetResponseAsync() as HttpWebResponse)
201 | {
202 | using (var stream = response.GetResponseStream())
203 | {
204 | using (var reader = new StreamReader(stream))
205 | {
206 | if (int.TryParse(reader.ReadToEnd(), out int n))
207 | {
208 | MainForm.Instance.OnVisits(n.ToString());
209 | }
210 | }
211 | }
212 | }
213 | }
214 | catch (WebException w)
215 | {
216 | MainForm.Instance.ShowMessage(w.Message);
217 | }
218 | catch (Exception x)
219 | {
220 | MainForm.Instance.ShowMessage(x.Message);
221 | }
222 | }
223 | }
224 |
225 | //
226 | // RemoteConfig
227 | //
228 |
229 | public class RemoteConfig
230 | {
231 | public string apiKey;
232 |
233 | public static void FetchConfig(Action callback)
234 | {
235 | VRCApi.Request(callback, "config");
236 | }
237 | }
238 |
239 | //
240 | // ApiUser
241 | //
242 |
243 | public class ApiUser
244 | {
245 | // for list sorting
246 | private static long Counter = 0;
247 | public long Index = ++Counter;
248 |
249 | public enum TrustLevel
250 | {
251 | None,
252 | Visitor,
253 | NewUser,
254 | User,
255 | KnownUser,
256 | TrustedUser,
257 | VeteranUser,
258 | Moderator
259 | }
260 |
261 | public class FriendStatus
262 | {
263 | public bool isFriend;
264 | public bool incomingRequest;
265 | public bool outgoingRequest;
266 | }
267 |
268 | public string id;
269 | public string displayName;
270 | public string username;
271 | public string location;
272 | public string currentAvatar;
273 | public string currentAvatarImageUrl;
274 | public string currentAvatarThumbnailImageUrl;
275 | public HashSet friends;
276 | public HashSet tags;
277 | public bool HasTag(string tag) => tags != null && tags.Contains(tag);
278 | public string status;
279 | public string statusDescription;
280 | public List friendGroupNames;
281 | public string last_login;
282 | public bool isFriend;
283 |
284 | // VRCEX variable
285 | public string locationInfo = string.Empty;
286 | public string pastLocation = string.Empty;
287 | public string pastLocationInfo = string.Empty;
288 | public TrustLevel trustLevel = TrustLevel.None;
289 |
290 | public static void Fetch(string id)
291 | {
292 | VRCApi.Request(MainForm.Instance.OnUser, $"users/{id}");
293 | }
294 |
295 | public static void FetchCurrentUser()
296 | {
297 | VRCApi.Request(MainForm.Instance.OnCurrentUser, "auth/user");
298 | }
299 |
300 | public static void FetchUsers(string search)
301 | {
302 | if (!string.IsNullOrEmpty(search))
303 | {
304 | VRCApi.Request>(MainForm.Instance.OnUsers, "users", ApiMethod.GET, new Dictionary
305 | {
306 | ["n"] = 25,
307 | ["offset"] = 0,
308 | ["search"] = search
309 | });
310 | }
311 | }
312 |
313 | private static void FetchFriends(Action> callback, Dictionary param, int count = 100, int offset = 0)
314 | {
315 | param["n"] = count;
316 | param["offset"] = offset;
317 | VRCApi.Request>((list) =>
318 | {
319 | if (list.Count == count)
320 | {
321 | FetchFriends(callback, param, count, offset + count);
322 | }
323 | callback.Invoke(list);
324 | }, "auth/user/friends", ApiMethod.GET, param);
325 | }
326 |
327 | public static void FetchFriends()
328 | {
329 | FetchFriends(MainForm.Instance.OnFriends, new Dictionary());
330 | FetchFriends(MainForm.Instance.OnFriends, new Dictionary()
331 | {
332 | ["offline"] = "true"
333 | });
334 | }
335 |
336 | public static void FetchAllFavoriteFriends()
337 | {
338 | VRCApi.Request>(MainForm.Instance.OnUsers, "auth/user/friends/favorite");
339 | }
340 |
341 | public static void SetStatus(string userId, string status, string statusDescription)
342 | {
343 | VRCApi.Request(MainForm.Instance.OnSetStatus, $"users/{userId}", ApiMethod.PUT, new Dictionary()
344 | {
345 | ["status"] = status,
346 | ["statusDescription"] = statusDescription
347 | });
348 | }
349 |
350 | // LoadFriendStatus
351 | public static void CheckFriendStatus(string userId)
352 | {
353 | if (!string.IsNullOrEmpty(userId))
354 | {
355 | VRCApi.Request((status) => MainForm.Instance.OnFriendStatus(userId, status), $"user/{userId}/friendStatus");
356 | }
357 | }
358 |
359 | // AddFriend
360 | public static void SendFriendRequest(string userId)
361 | {
362 | if (!string.IsNullOrEmpty(userId))
363 | {
364 | VRCApi.Request((response) => MainForm.Instance.OnSendFriendRequest(userId, response), $"user/{userId}/friendRequest", ApiMethod.POST);
365 | }
366 | }
367 |
368 | public static void CancelFriendRequest(string userId)
369 | {
370 | if (!string.IsNullOrEmpty(userId))
371 | {
372 | VRCApi.Request((response) => MainForm.Instance.OnCancelFriendRequest(userId, response), $"user/{userId}/friendRequest", ApiMethod.DELETE);
373 | }
374 | }
375 |
376 | public static void AcceptFriendRequest(string notificationId)
377 | {
378 | if (!string.IsNullOrEmpty(notificationId))
379 | {
380 | VRCApi.Request((response) => MainForm.Instance.OnAcceptFriendRequest(notificationId, response), $"auth/user/notifications/{notificationId}/accept", ApiMethod.PUT);
381 | }
382 | }
383 |
384 | public static void DeclineFriendRequest(string notificationId)
385 | {
386 | if (!string.IsNullOrEmpty(notificationId))
387 | {
388 | VRCApi.Request(MainForm.Instance.OnDeclineFriendRequest, $"auth/user/notifications/{notificationId}/hide", ApiMethod.PUT);
389 | }
390 | }
391 |
392 | // RemoveFriend
393 | public static void UnfriendUser(string userId)
394 | {
395 | if (!string.IsNullOrEmpty(userId))
396 | {
397 | VRCApi.Request((response) => MainForm.Instance.OnUnfriendUser(userId, response), $"auth/user/friends/{userId}", ApiMethod.DELETE);
398 | }
399 | }
400 |
401 | public string GetFriendsGroupDisplayName(int index)
402 | {
403 | if (friendGroupNames != null && index < 3 && index < friendGroupNames.Count && !string.IsNullOrEmpty(friendGroupNames[index]))
404 | {
405 | return friendGroupNames[index];
406 | }
407 | return "Group " + (index + 1);
408 | }
409 |
410 | public TrustLevel GetTrustLevel()
411 | {
412 | if (trustLevel == TrustLevel.None)
413 | {
414 | // admin_scripting_access -> Scripting Access
415 | // admin_avatar_access -> Forced Avatar Access
416 | // admin_world_access -> Forced World Access
417 | // admin_lock_tags -> Locked Tags
418 | // admin_lock_level -> Locked Level
419 | // system_world_access -> SDK World Access
420 | // system_avatar_access -> SDK Avatar Access
421 | // system_feedback_access -> Feedback Access
422 | // system_legend -> Legendary User
423 | // system_probable_troll -> Probable Troll
424 | // system_troll -> Troll
425 | if (HasTag("admin_moderator"))
426 | {
427 | trustLevel = TrustLevel.Moderator;
428 | }
429 | else if (HasTag("system_trust_legend"))
430 | {
431 | trustLevel = TrustLevel.VeteranUser;
432 | }
433 | else if (HasTag("system_trust_veteran"))
434 | {
435 | trustLevel = TrustLevel.TrustedUser;
436 | }
437 | else if (HasTag("system_trust_trusted"))
438 | {
439 | trustLevel = TrustLevel.KnownUser;
440 | }
441 | else if (HasTag("system_trust_known"))
442 | {
443 | trustLevel = TrustLevel.User;
444 | }
445 | else if (HasTag("system_trust_basic"))
446 | {
447 | trustLevel = TrustLevel.NewUser;
448 | }
449 | else
450 | {
451 | trustLevel = TrustLevel.Visitor;
452 | }
453 | }
454 | return trustLevel;
455 | }
456 |
457 | /*
458 | public Color vipNamePlateColor = new Color(0.709803939f, 0.149019614f, 0.149019614f);
459 | public Color friendNamePlateColor = new Color(1f, 1f, 0f);
460 | public Color untrustedNamePlateColor = new Color(0.8f, 0.8f, 0.8f);
461 | public Color basicNamePlateColor = new Color(0.09019608f, 0.470588237f, 1f);
462 | public Color knownNamePlateColor = new Color(0.168627456f, 0.8117647f, 0.360784322f);
463 | public Color trustedNamePlateColor = new Color(1f, 0.482352942f, 0.258823544f);
464 | public Color veteranNamePlateColor = new Color(0.5058824f, 0.2627451f, 0.9019608f);
465 | public Color legendNamePlateColor = new Color(1f, 1f, 0f);
466 | public Color trollNamePlateColor = new Color(0.470588237f, 0.184313729f, 0.184313729f);
467 | public Color mutedNamePlateColor = new Color(0.5f, 0.5f, 0.5f, 0.6f);
468 | */
469 |
470 | private static readonly Color FriendColor = Color.FromArgb(255, 255, 0);
471 | private static readonly Color UntrustedColor = Color.FromArgb(128, 128, 128); // (204, 204, 204)
472 | private static readonly Color BasicColor = Color.FromArgb(23, 120, 255);
473 | private static readonly Color KnownColor = Color.FromArgb(43, 207, 92);
474 | private static readonly Color TrustedColor = Color.FromArgb(255, 123, 66);
475 | private static readonly Color VeteranColor = Color.FromArgb(129, 67, 230);
476 | private static readonly Color LegendColor = Color.FromArgb(255, 215, 0); // (255, 255, 0)
477 | private static readonly Color VIPColor = Color.FromArgb(255, 0, 0); // Color.FromArgb(181, 38, 38);
478 | private static readonly Color TrollColor = Color.FromArgb(120, 47, 47);
479 |
480 | public Color GetColor()
481 | {
482 | switch (GetTrustLevel())
483 | {
484 | case TrustLevel.NewUser:
485 | return BasicColor;
486 | case TrustLevel.User:
487 | return KnownColor;
488 | case TrustLevel.KnownUser:
489 | return TrustedColor;
490 | case TrustLevel.TrustedUser:
491 | return VeteranColor;
492 | case TrustLevel.VeteranUser:
493 | return LegendColor;
494 | case TrustLevel.Moderator:
495 | return VIPColor;
496 | }
497 | return UntrustedColor;
498 | }
499 |
500 | public static void SendMessage(string userId, string message)
501 | {
502 | if (!string.IsNullOrEmpty(userId) &&
503 | !string.IsNullOrEmpty(message))
504 | {
505 | // ApiNotification이긴한데 오류가 나길래 귀찮아서 걍 함
506 | VRCApi.Request((response) => MainForm.Instance.OnSendMessage(userId, response), $"user/{userId}/message", ApiMethod.POST, new Dictionary
507 | {
508 | ["type"] = "message",
509 | ["message"] = message
510 | });
511 | }
512 | }
513 | }
514 |
515 | //
516 | // ApiWorld
517 | //
518 |
519 | public class ApiWorld
520 | {
521 | public string id;
522 | public string name;
523 | public string imageUrl;
524 | public string thumbnailImageUrl;
525 | public string authorName;
526 | public string releaseStatus;
527 | public int capacity;
528 | public int occupants;
529 | public string authorId;
530 | public string assetUrl;
531 | public string description;
532 | public string unityPackageUrl;
533 | public List> instances;
534 | public int favorites;
535 | public int visits;
536 |
537 | public static void Fetch(string id)
538 | {
539 | if (!string.IsNullOrEmpty(id))
540 | {
541 | VRCApi.Request(MainForm.Instance.OnWorld, $"worlds/{id}");
542 | }
543 | }
544 |
545 | public static void FetchList(string search)
546 | {
547 | if (!string.IsNullOrEmpty(search))
548 | {
549 | VRCApi.Request>(MainForm.Instance.OnWorlds, "worlds", ApiMethod.GET, new Dictionary
550 | {
551 | ["n"] = 100,
552 | ["offset"] = 0,
553 | ["sort"] = "popularity",
554 | ["order"] = "descending",
555 | ["search"] = search,
556 | ["releaseStatus"] = "public"
557 | });
558 | }
559 | }
560 |
561 | public static void FetchMyList()
562 | {
563 | VRCApi.Request>(MainForm.Instance.OnWorlds, "worlds", ApiMethod.GET, new Dictionary
564 | {
565 | ["n"] = 100,
566 | ["offset"] = 0,
567 | ["user"] = "me",
568 | ["sort"] = "updated",
569 | ["order"] = "descending",
570 | ["releaseStatus"] = "all"
571 | });
572 | }
573 |
574 | public static void FetchListByUser(string userId)
575 | {
576 | if (!string.IsNullOrEmpty(userId))
577 | {
578 | VRCApi.Request>(MainForm.Instance.OnWorlds, "worlds", ApiMethod.GET, new Dictionary
579 | {
580 | ["n"] = 100,
581 | ["offset"] = 0,
582 | ["userId"] = userId,
583 | ["sort"] = "updated",
584 | ["order"] = "descending",
585 | ["releaseStatus"] = "public"
586 | });
587 | }
588 | }
589 |
590 | public static void FetchRecentList()
591 | {
592 | VRCApi.Request>(MainForm.Instance.OnWorlds, "worlds/recent", ApiMethod.GET, new Dictionary
593 | {
594 | ["n"] = 100,
595 | ["offset"] = 0,
596 | ["releaseStatus"] = "all"
597 | });
598 | }
599 |
600 | public static void FetchFavoriteList()
601 | {
602 | VRCApi.Request>(MainForm.Instance.OnWorlds, "worlds/favorites");
603 | }
604 | }
605 |
606 | public class ApiWorldInstance
607 | {
608 | public string id;
609 | public List users;
610 |
611 | public static void Fetch(string worldId, string instanceId)
612 | {
613 | if (!string.IsNullOrEmpty(worldId) &&
614 | !string.IsNullOrEmpty(instanceId))
615 | {
616 | VRCApi.Request(MainForm.Instance.OnWorldInstance, $"worlds/{worldId}/{instanceId}");
617 | }
618 | }
619 | }
620 |
621 | //
622 | // ApiAvatar
623 | //
624 |
625 | public class ApiAvatar
626 | {
627 | public string id;
628 | public string name;
629 | public string imageUrl;
630 | public string authorName;
631 | public string authorId;
632 | public string assetUrl;
633 | public string description;
634 | public string unityPackageUrl;
635 | public string thumbnailImageUrl;
636 | public string releaseStatus;
637 |
638 | public static void Fetch(string id)
639 | {
640 | if (!string.IsNullOrEmpty(id))
641 | {
642 | VRCApi.Request(MainForm.Instance.OnAvatar, $"avatars/{id}");
643 | }
644 | }
645 |
646 | private static void FetchList(Action> callback, Dictionary param, int count = 100, int offset = 0)
647 | {
648 | param["n"] = count;
649 | param["offset"] = offset;
650 | VRCApi.Request>((list) =>
651 | {
652 | if (list.Count == count && offset + count < 500)
653 | {
654 | FetchList(callback, param, count, offset + count);
655 | }
656 | callback.Invoke(list);
657 | }, "avatars", ApiMethod.GET, param);
658 | }
659 |
660 | public static void FetchMyList()
661 | {
662 | FetchList(MainForm.Instance.OnAvatars, new Dictionary
663 | {
664 | ["user"] = "me",
665 | ["sort"] = "updated",
666 | ["order"] = "descending",
667 | ["releaseStatus"] = "all"
668 | });
669 | }
670 |
671 | public static void FetchListByUser(string userId)
672 | {
673 | if (!string.IsNullOrEmpty(userId))
674 | {
675 | FetchList(MainForm.Instance.OnAvatars, new Dictionary
676 | {
677 | ["userId"] = userId,
678 | ["sort"] = "updated",
679 | ["order"] = "descending",
680 | ["releaseStatus"] = "public"
681 | });
682 | }
683 | }
684 |
685 | public static void FetchFavoriteList()
686 | {
687 | VRCApi.Request>(MainForm.Instance.OnAvatars, "avatars/favorites");
688 | }
689 |
690 | public static void AssignToThisUser(string avatarId)
691 | {
692 | if (!string.IsNullOrEmpty(avatarId))
693 | {
694 | VRCApi.Request(MainForm.Instance.OnCurrentUser, $"avatars/{avatarId}/select", ApiMethod.PUT);
695 | }
696 | }
697 | }
698 |
699 | //
700 | // ApiFavorite
701 | //
702 |
703 | public class ApiFavorite
704 | {
705 | public enum FavoriteType
706 | {
707 | Friend,
708 | World,
709 | Avatar
710 | }
711 |
712 | public string id;
713 | public string type;
714 | public string favoriteId;
715 | public HashSet tags;
716 |
717 | public static void FetchList(FavoriteType type, string tags = null)
718 | {
719 | var param = new Dictionary
720 | {
721 | ["n"] = 100,
722 | ["type"] = type.ToString().ToLower()
723 | };
724 | if (!string.IsNullOrEmpty(tags))
725 | {
726 | param["tags"] = tags;
727 | }
728 | VRCApi.Request>((list) => MainForm.Instance.OnFavorites(type, list), "favorites", ApiMethod.GET, param);
729 | }
730 |
731 | public static void AddFavorite(string objectId, FavoriteType type, string tags = null)
732 | {
733 | if (!string.IsNullOrEmpty(objectId))
734 | {
735 | var param = new Dictionary()
736 | {
737 | ["type"] = type.ToString().ToLower(),
738 | ["favoriteId"] = objectId
739 | };
740 | if (!string.IsNullOrEmpty(tags))
741 | {
742 | param["tags"] = tags;
743 | }
744 | VRCApi.Request(MainForm.Instance.OnAddFavorite, "favorites", ApiMethod.POST, param);
745 | }
746 | }
747 |
748 | public static void RemoveFavorite(string objectId)
749 | {
750 | if (!string.IsNullOrEmpty(objectId))
751 | {
752 | VRCApi.Request(MainForm.Instance.OnRemoveFavorite, $"favorites/{objectId}", ApiMethod.DELETE);
753 | }
754 | }
755 | }
756 |
757 | //
758 | // ApiPlayerModeration
759 | //
760 |
761 | public class ApiPlayerModeration
762 | {
763 | public enum ModerationType
764 | {
765 | None,
766 | Block,
767 | Mute,
768 | Unmute,
769 | HideAvatar,
770 | ShowAvatar
771 | }
772 |
773 | public string id;
774 | public string type;
775 | public string targetUserId;
776 | public string targetDisplayName;
777 | public string sourceUserId;
778 | public string sourceDisplayName;
779 | public string created;
780 |
781 | public static void FetchAllMine()
782 | {
783 | // VRCApi.Request>(MainForm.Instance.OnPlayerModerations, "auth/user/playermoderations");
784 | VRCApi.Request>(MainForm.Instance.OnPlayerModerations, "auth/user/playermoderations?type=block");
785 | VRCApi.Request>(MainForm.Instance.OnPlayerModerations, "auth/user/playermoderations?type=mute");
786 | VRCApi.Request>(MainForm.Instance.OnPlayerModerations, "auth/user/playermoderations?type=hideAvatar");
787 | }
788 |
789 | public static void FetchAllAgainstMe()
790 | {
791 | VRCApi.Request>(MainForm.Instance.OnPlayerModerationsAgainstMe, "auth/user/playermoderated");
792 | }
793 |
794 | public static void SendModeration(string targetUserId, ModerationType type)
795 | {
796 | if (!string.IsNullOrEmpty(targetUserId))
797 | {
798 | VRCApi.Request(MainForm.Instance.OnSendModeration, "auth/user/playermoderations", ApiMethod.POST, new Dictionary
799 | {
800 | ["moderated"] = targetUserId,
801 | ["type"] = ModerationTypeToAPIString(type)
802 | });
803 | }
804 | }
805 |
806 | public static void DeleteModeration(string targetUserId, ModerationType type)
807 | {
808 | if (!string.IsNullOrEmpty(targetUserId))
809 | {
810 | VRCApi.Request(MainForm.Instance.OnDeleteModeration, "auth/user/unplayermoderate", ApiMethod.PUT, new Dictionary
811 | {
812 | ["moderated"] = targetUserId,
813 | ["type"] = ModerationTypeToAPIString(type)
814 | });
815 | }
816 | }
817 |
818 | public static string ModerationTypeToAPIString(ModerationType type)
819 | {
820 | switch (type)
821 | {
822 | case ModerationType.ShowAvatar:
823 | return "showAvatar";
824 | case ModerationType.HideAvatar:
825 | return "hideAvatar";
826 | default:
827 | return type.ToString().ToLower();
828 | }
829 | }
830 | }
831 |
832 | //
833 | // ApiNotification
834 | //
835 |
836 | public class ApiNotification
837 | {
838 | public enum NotificationType
839 | {
840 | All,
841 | Message,
842 | FriendRequest,
843 | Invite,
844 | RequestInvite,
845 | VoteToKick,
846 | Halp,
847 | Hidden
848 | }
849 |
850 | public string id;
851 | public string senderUserId;
852 | public string senderUsername;
853 | public string type;
854 | public string message;
855 | public string created_at;
856 | public string details;
857 |
858 | private static void FetchAll(Action> callback, Dictionary param, int count = 100, int offset = 0)
859 | {
860 | param["n"] = count;
861 | param["offset"] = offset;
862 | VRCApi.Request>((list) =>
863 | {
864 | if (list.Count == count)
865 | {
866 | FetchAll(callback, param, count, offset + count);
867 | }
868 | callback.Invoke(list);
869 | }, "auth/user/notifications", ApiMethod.GET, param);
870 | }
871 |
872 | public static void FetchAll(NotificationType type, string afterString)
873 | {
874 | var param = new Dictionary();
875 | if (type != NotificationType.All)
876 | {
877 | param["type"] = (type == NotificationType.FriendRequest) ? "friendRequest" : type.ToString().ToLower();
878 | }
879 | if (!string.IsNullOrEmpty(afterString))
880 | {
881 | param["after"] = afterString;
882 | }
883 | FetchAll(MainForm.Instance.OnNotifications, param);
884 | }
885 |
886 | /*public static void MarkNotificationAsSeen(string notificationId)
887 | {
888 | if (!string.IsNullOrEmpty(notificationId))
889 | {
890 | Http.Request(MainForm.Instance.OnMarkNotificationAsSeen, $"auth/user/notifications/{notificationId}/see", RequestMethod.Put);
891 | }
892 | }*/
893 | }
894 |
895 | //
896 | // ApiFile
897 | //
898 |
899 | public class ApiFile
900 | {
901 | public class Version
902 | {
903 | public class FileDescriptor
904 | {
905 | public string url;
906 | public int sizeInBytes;
907 | }
908 |
909 | public string created_at;
910 | public FileDescriptor file;
911 | }
912 |
913 | public string id;
914 | public List versions;
915 |
916 | public static void Fetch(string id, object arg)
917 | {
918 | if (!string.IsNullOrEmpty(id))
919 | {
920 | VRCApi.Request((file) => MainForm.Instance.OnFile(arg, file), $"file/{id}");
921 | }
922 | }
923 |
924 | public static void ParseAndFetch(string url, object tag)
925 | {
926 | if (!string.IsNullOrEmpty(url))
927 | {
928 | var begin = url.IndexOf("/api/1/file/");
929 | if (begin >= 0)
930 | {
931 | var end = url.IndexOf('/', begin += 12);
932 | if (end < 0)
933 | {
934 | end = url.Length;
935 | }
936 | if (begin < end)
937 | {
938 | Fetch(url.Substring(begin, end - begin), tag);
939 | }
940 | }
941 | }
942 | }
943 | }
944 |
945 | //
946 | // LocationInfo
947 | //
948 |
949 | public class LocationInfo
950 | {
951 | public string WorldId = string.Empty;
952 | public string InstanceId = string.Empty;
953 | public string InstanceInfo = string.Empty;
954 |
955 | public static LocationInfo Parse(string idWithTags, bool strict = true)
956 | {
957 | // offline
958 | // private
959 | // local:0000000000
960 | // Public wrld_785bee79-b83b-449c-a3d9-f1c5a29bcd3d:12502
961 | // Friends+ wrld_785bee79-b83b-449c-a3d9-f1c5a29bcd3d:12502~hidden(usr_4f76a584-9d4b-46f6-8209-8305eb683661)~nonce(79985ba6-8804-49dd-8c8a-c86fe817caca)
962 | // Friends wrld_785bee79-b83b-449c-a3d9-f1c5a29bcd3d:12502~friends(usr_4f76a584-9d4b-46f6-8209-8305eb683661)~nonce(13374166-629e-4ac5-afe9-29637719d78c)
963 | // Invite+ wrld_785bee79-b83b-449c-a3d9-f1c5a29bcd3d:12502~private(usr_4f76a584-9d4b-46f6-8209-8305eb683661)~nonce(6d9b02ca-f32c-4360-b8ac-9996bf12fd74)~canRequestInvite
964 | // Invite wrld_785bee79-b83b-449c-a3d9-f1c5a29bcd3d:12502~private(usr_4f76a584-9d4b-46f6-8209-8305eb683661)~nonce(5db0f688-4211-428b-83c5-91533e0a5d5d)
965 | // wrld_가 아니라 wld_인 것들도 있고 예전 맵들의 경우 아예 o_나 b_인것들도 있음; 그냥 uuid형태인 것들도 있고 개판임
966 | var tags = idWithTags.Split('~');
967 | var a = tags[0].Split(new char[] { ':' }, 2);
968 | if (!string.IsNullOrEmpty(a[0]))
969 | {
970 | if (a.Length == 2)
971 | {
972 | if (!string.IsNullOrEmpty(a[1]) &&
973 | !"local".Equals(a[0]))
974 | {
975 | var L = new LocationInfo
976 | {
977 | WorldId = a[0]
978 | };
979 | var type = "public";
980 | if (tags.Length > 1)
981 | {
982 | var tag = "~" + string.Join("~", tags, 1, tags.Length - 1);
983 | if (tag.Contains("~private("))
984 | {
985 | if (tag.Contains("~canRequestInvite"))
986 | {
987 | type = "invite+"; // Invite Plus
988 | }
989 | else
990 | {
991 | type = "invite"; // Invite Only
992 | }
993 | }
994 | else if (tag.Contains("~friends("))
995 | {
996 | type = "friends"; // Friends Only
997 | }
998 | else if (tag.Contains("~hidden("))
999 | {
1000 | type = "friends+"; // Friends of Guests
1001 | }
1002 | L.InstanceId = a[1] + tag;
1003 | }
1004 | else
1005 | {
1006 | L.InstanceId = a[1];
1007 | }
1008 | L.InstanceInfo = $"#{a[1]} {type}";
1009 | return L;
1010 | }
1011 | }
1012 | else if (!strict && !("offline".Equals(a[0]) || "private".Equals(a[0])))
1013 | {
1014 | return new LocationInfo()
1015 | {
1016 | WorldId = a[0]
1017 | };
1018 | }
1019 | }
1020 | return null;
1021 | }
1022 | }
1023 | }
--------------------------------------------------------------------------------
/VRChatRPC.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using System.Text;
4 |
5 | namespace VRCEX
6 | {
7 | public static class VRChatRPC
8 | {
9 | [DllImport("VRChatRPC", CallingConvention = CallingConvention.Cdecl)]
10 | private static extern bool VRChatRPC_000();
11 |
12 | [DllImport("VRChatRPC", CallingConvention = CallingConvention.Cdecl)]
13 | private static extern int VRChatRPC_001([Out] byte[] data, int size);
14 |
15 | [DllImport("VRChatRPC", CallingConvention = CallingConvention.Cdecl)]
16 | private static extern IntPtr VRChatRPC_002();
17 |
18 | public static bool Update()
19 | {
20 | return VRChatRPC_000();
21 | }
22 |
23 | public static string GetAuthSessionTicket()
24 | {
25 | var a = new byte[1024];
26 | var n = VRChatRPC_001(a, 1024);
27 | return BitConverter.ToString(a, 0, n).Replace("-", string.Empty);
28 | }
29 |
30 | public static string GetPersonaName()
31 | {
32 | var ptr = VRChatRPC_002();
33 | if (ptr != IntPtr.Zero)
34 | {
35 | int n = 0;
36 | while (Marshal.ReadByte(ptr, n) != 0)
37 | {
38 | ++n;
39 | }
40 | if (n > 0)
41 | {
42 | var a = new byte[n];
43 | Marshal.Copy(ptr, a, 0, a.Length);
44 | return Encoding.UTF8.GetString(a);
45 | }
46 | }
47 | return string.Empty;
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/VRChatRPC/README.txt:
--------------------------------------------------------------------------------
1 | it grabs some data from VRChat process.. use at own risk.
2 |
3 | by pypy (mina#5656)
--------------------------------------------------------------------------------
/VRChatRPC/VRChatRPC.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pypy-vrc/VRCEX/2a511ccd7b3418410560f28912b6ba61389eac50/VRChatRPC/VRChatRPC.dll
--------------------------------------------------------------------------------
/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/vrchat.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pypy-vrc/VRCEX/2a511ccd7b3418410560f28912b6ba61389eac50/vrchat.ico
--------------------------------------------------------------------------------