├── .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 | ![0](https://user-images.githubusercontent.com/25771678/53702922-73580e80-3e4f-11e9-9f20-d177cfe2c208.png) 8 | ![1](https://user-images.githubusercontent.com/25771678/53702901-43107000-3e4f-11e9-88ed-b51488224fe9.png) 9 | ![2](https://user-images.githubusercontent.com/25771678/53702902-43a90680-3e4f-11e9-9335-199757441551.png) 10 | ![3](https://user-images.githubusercontent.com/25771678/53702903-44da3380-3e4f-11e9-9cd8-0de4ebeab14e.png) 11 | ![4](https://user-images.githubusercontent.com/25771678/53702905-4572ca00-3e4f-11e9-9956-8aebf3276e0a.png) 12 | ![5](https://user-images.githubusercontent.com/25771678/53702906-460b6080-3e4f-11e9-958b-8e8d1223ca09.png) 13 | ![6](https://user-images.githubusercontent.com/25771678/53702907-473c8d80-3e4f-11e9-9bd4-21a23cec3f6b.png) 14 | ![7](https://user-images.githubusercontent.com/25771678/53702908-473c8d80-3e4f-11e9-8373-76048c9b044a.png) 15 | ![8](https://user-images.githubusercontent.com/25771678/53702909-499ee780-3e4f-11e9-8d4d-f3f811f8cae7.png) 16 | ![9](https://user-images.githubusercontent.com/25771678/53702910-4a377e00-3e4f-11e9-8331-1c38077448fc.png) 17 | ![10](https://user-images.githubusercontent.com/25771678/57005322-2eafe000-6c12-11e9-8062-02cf8d409b79.PNG) 18 | ![11](https://user-images.githubusercontent.com/25771678/57005324-3079a380-6c12-11e9-8845-b58b71a4dcae.PNG) 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 --------------------------------------------------------------------------------