├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── valmeter.rmskin └── valmeter ├── @Resources ├── Assets │ └── icon.png ├── Fonts │ └── Rubik.ttf ├── Includes │ ├── processes.inc │ └── style.inc └── Scripts │ ├── package.json │ ├── presence.js │ └── runner.lua └── valmeter.ini /.gitattributes: -------------------------------------------------------------------------------- 1 | *.lua linguist-language=Lua 2 | *.ini linguist-language=Pawn 3 | *.inc linguist-language=Pawn 4 | *.ps1 linguist-language=Powershell 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | node_modules/ 3 | package-lock.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 frissyn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # valmeter 2 | 3 | **Fully-featured Discord RPC for VALORANT powered by Rainmeter and Lua.** 4 | 5 | [*Still a work in progress, not worth using yet*] 6 | 7 | ## Installation 8 | 9 | 1. Install the latest stable version of Rainmeter if you don't have it already. [`Download`](https://rainmeter.net). 10 | 2. Go to the latest version in [`Releases`](https://github.com/frissyn/valmeter/releases) and zownload the `.zip` file. Unzip the folder. 11 | 3. From here you can do two things: 12 | + **Recommended:** 13 | 1. Launch the `.rmskin` installer 14 | 2. Install the skin, Rainmeter will auto-build it. 15 | + **Alternative:** 16 | 1. Move the `valmeter` skin folder into your `Rainmeter/Skins` folder. 17 | 2. Configure the skin how you want to. 18 | 19 | ## Usage 20 | 21 | Load the skin from Rainmeter's skin menu. The skin UI should appear on your desktop if everything was installed properly. `valmeter` will automatically watch your computer's open processes and start the Rich Prescense when `RiotClientServices.exe` and `VALORANT.exe` are running. If you want to turn off valmeter, just `Unload` the Skin after right-clicking it. 22 | 23 | ## Contributing 24 | 25 | 1. Fork the repository: [`Fork`](https://github.com/frissyn/valmeter/fork) 26 | 2. Create your feature branch (`git checkout -b my-new-feature`) 27 | 3. Commit your changes (`git commit -am 'Add some feature'`) 28 | 4. Push to the branch (`git push origin my-new-feature`) 29 | 5. Create a new Pull Request! 🎉 30 | -------------------------------------------------------------------------------- /valmeter.rmskin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frissyn/valmeter/a58b27759bbd58e3b17cf04361f3c1868bb529d0/valmeter.rmskin -------------------------------------------------------------------------------- /valmeter/@Resources/Assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frissyn/valmeter/a58b27759bbd58e3b17cf04361f3c1868bb529d0/valmeter/@Resources/Assets/icon.png -------------------------------------------------------------------------------- /valmeter/@Resources/Fonts/Rubik.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frissyn/valmeter/a58b27759bbd58e3b17cf04361f3c1868bb529d0/valmeter/@Resources/Fonts/Rubik.ttf -------------------------------------------------------------------------------- /valmeter/@Resources/Includes/processes.inc: -------------------------------------------------------------------------------- 1 | [ClientProcess] 2 | Measure=Plugin 3 | Plugin=Process 4 | ProcessName=RiotClientServices.exe 5 | Substitute="-1":"Not Running","1":"Running" 6 | 7 | [GameProcess] 8 | Measure=Plugin 9 | Plugin=Process 10 | ProcessName=VALORANT.exe 11 | Substitute="-1":"Not Running","1":"Running" 12 | 13 | [RPCScript] 14 | Measure=Script 15 | ScriptFile=[#@]Scripts/runner.lua 16 | -------------------------------------------------------------------------------- /valmeter/@Resources/Includes/style.inc: -------------------------------------------------------------------------------- 1 | [Variables] 2 | Blurple="7289DA" 3 | White="FFFFFF" 4 | Greyple="7289DA" 5 | Dark="2C2F33" 6 | KindaBlack="23272A" 7 | Black="000000" 8 | PrimaryFont="Rubik" 9 | -------------------------------------------------------------------------------- /valmeter/@Resources/Scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "valmeter", 3 | "version": "0.0.1", 4 | "description": "Fully-featured Discord RPC for VALORANT powered by Rainmeter and Lua.", 5 | "main": "presence.js", 6 | "scripts": { 7 | "start": "node ." 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/frissyn/valmeter.git" 12 | }, 13 | "author": "frissyn", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/frissyn/valmeter/issues" 17 | }, 18 | "homepage": "https://github.com/frissyn/valmeter#readme", 19 | "dependencies": { 20 | "axios": "^0.21.1", 21 | "discord-rich-presence": "0.0.8" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /valmeter/@Resources/Scripts/presence.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const https = require("https"); 3 | const axios = require("axios").default; 4 | const RPC = require("discord-rich-presence")("821424938526441562"); 5 | 6 | const GAME_DATA = { 7 | maps: { 8 | "Port": "Icebox", 9 | "Duality": "Bind", 10 | "Bonsai": "Split", 11 | "Ascent": "Ascent", 12 | "Triad": "Haven", 13 | "Range": "The Range" 14 | }, 15 | queues: { 16 | "unrated":"Unrated", 17 | "competitive":"Competitive", 18 | "spikerush":"Spike Rush", 19 | "deathmatch":"Deathmatch", 20 | "ggteam":"Escalation" 21 | }, 22 | agents: { 23 | "5f8d3a7f-467b-97f3-062c-13acf203c006":"Breach", 24 | "f94c3b30-42be-e959-889c-5aa313dba261":"Raze", 25 | "6f2a04ca-43e0-be17-7f36-b3908627744d":"Skye", 26 | "117ed9e3-49f3-6512-3ccf-0cada7e3823b":"Cypher", 27 | "320b2a48-4d9b-a075-30f1-1f93a9b638fa":"Sova", 28 | "1e58de9c-4950-5125-93e9-a0aee9f98746":"Killjoy", 29 | "707eab51-4836-f488-046a-cda6bf494859":"Viper", 30 | "eb93336a-449b-9c1b-0a54-a891f7921d69":"Phoenix", 31 | "41fb69c1-4189-7b37-f117-bcaf1e96f1bf":"Astra", 32 | "9f0d8ba9-4140-b941-57d3-a7ad57c6b417":"Brimstone", 33 | "7f94d92c-4234-0a36-9646-3a87eb8b5c89":"Yoru", 34 | "569fdd95-4d10-43ab-ca70-79becc718b46":"Sage", 35 | "a3bfb853-43b2-7238-a4f1-ad90e9e46bcc":"Reyna", 36 | "8e253930-4c05-31dd-1b6c-968525494517":"Omen", 37 | "add6443a-41bd-e414-f6ad-e58d267f4e95":"Jett" 38 | } 39 | } 40 | 41 | class LocalValorantClient { 42 | constructor (path = null) { 43 | if (!path) { 44 | const appData = process.env.LOCALAPPDATA; 45 | path = `${appData}\\Riot Games\\Riot Client\\Config\\lockfile`; 46 | } 47 | 48 | const data = fs.readFileSync(path, "utf-8").split(":"); 49 | 50 | this.auth = Buffer.from(`riot:${data[3]}`).toString("base64"); 51 | this.axios = axios.create({ 52 | baseURL: `https://127.0.0.1:${data[2]}`, 53 | headers: {"Authorization": `Basic ${this.auth}`}, 54 | httpsAgent: new https.Agent({rejectUnauthorized: false}) 55 | }); 56 | } 57 | 58 | getSession() { 59 | return this.axios.get("chat/v1/session").then((res) => { 60 | return res.data; 61 | }) 62 | } 63 | 64 | getPresences() { 65 | return this.axios.get("chat/v4/presences").then((res) => { 66 | return res.data["presences"]; 67 | }) 68 | } 69 | 70 | async getGamePresence() { 71 | const session = await this.getSession(); 72 | const presences = await this.getPresences(); 73 | 74 | for (let p of presences) { 75 | if (p["puuid"] == session["puuid"]) { 76 | let result = Buffer.from(p["private"], "base64"); 77 | return JSON.parse(result.toString("ascii")); 78 | } 79 | } 80 | } 81 | 82 | parseToRPC(presence) { 83 | let result = {instance: true, largeImageKey: "valorant", raw: presence}; 84 | 85 | result["partySize"] = presence["partySize"]; 86 | result["partyMax"] = presence["maxPartySize"]; 87 | 88 | if (presence["sessionLoopState"] == "MENUS") { 89 | if (presence["isIdle"]) { 90 | result["state"] = "Idling"; 91 | result["details"] = "In Game Menu"; 92 | } else { 93 | result["state"] = "Looking For Match"; 94 | result["details"] = `${GAME_DATA.queues[presence["queueId"]]} Lobby`; 95 | } 96 | } else if (presence["sessionLoopState"] == "INGAME") { 97 | let m = presence["matchMap"].split("/"); 98 | let mapName = GAME_DATA.maps[m[m.length - 1]] 99 | 100 | result["state"] = `${GAME_DATA.queues[presence["queueId"]]}`; 101 | result["details"] = `${mapName} (${presence["partyOwnerMatchScoreAllyTeam"]} - ${presence["partyOwnerMatchScoreEnemyTeam"]})` 102 | // result["startTimestamp"] = Date.parse(presence["queueEntryTime"].replace(".", " ")); 103 | } 104 | 105 | return result; 106 | } 107 | } 108 | 109 | 110 | const resArray = new Array; 111 | const client = new LocalValorantClient(); 112 | 113 | console.log("Started listening for RPC Updates."); 114 | 115 | setInterval(() => { 116 | client.getGamePresence() 117 | .then((res) => { 118 | const data = client.parseToRPC(res) 119 | RPC.updatePresence(client.parseToRPC(res)) 120 | console.log("RPC Updated. -- ", res["queueEntryTime"].replace("\.", " ")) 121 | }) 122 | .catch((err) => { 123 | console.log("Encoutered Error: ", err) 124 | console.log("Exiting silently...") 125 | process.exit(1) 126 | }) 127 | }, 1050); 128 | -------------------------------------------------------------------------------- /valmeter/@Resources/Scripts/runner.lua: -------------------------------------------------------------------------------- 1 | function Initialize() 2 | Running = false 3 | Location = SKIN:GetVariable("@").."Scripts" 4 | Script = SKIN:GetVariable("@").."Scripts\\presence.js" 5 | end 6 | 7 | function Update() 8 | local gameProc = SKIN:GetMeasure('GameProcess'):GetValue() 9 | local clientProc = SKIN:GetMeasure('ClientProcess'):GetValue() 10 | 11 | if gameProc == 0 and clientProc == 1 then 12 | SKIN:Bang('!SetOption', 'RPCMeter', 'Text', 'RPC: Loading...') 13 | elseif gameProc == 1 and clientProc == 1 then 14 | SKIN:Bang('!SetOption', 'RPCMeter', 'Text', 'RPC: Active!') 15 | 16 | if not Running then 17 | Running = true 18 | os.execute( 19 | table.concat({ 20 | [[start "NPM" %ComSpec% /D /E:ON /K]], 21 | string.format([["call cd %s &]], Location), 22 | [[call npm.cmd i &]], 23 | [[call npm.cmd start"]] 24 | }) 25 | ) 26 | end 27 | else 28 | SKIN:Bang('!SetOption', 'RPCMeter', 'Text', 'RPC: Inactive!') 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /valmeter/valmeter.ini: -------------------------------------------------------------------------------- 1 | ; ------------------------------------------- 2 | ; Valmeter by frissyn 3 | ; github.com/frissyn/valmeter 4 | ; ------------------------------------------- 5 | 6 | [Rainmeter] 7 | Update=750 8 | AccurateText=1 9 | DynamicWindowSize=1 10 | 11 | 12 | [Metadata] 13 | Author=frissyn 14 | Name=valmeter 15 | Information="Fully-featured Discord RPC for Valorant" 16 | Version=0.1 17 | License="MIT License" 18 | 19 | 20 | [Variables] 21 | @include=[#@]Includes/style.inc 22 | @include2=[#@]Includes/processes.inc 23 | 24 | 25 | [Background] 26 | Meter=Shape 27 | Shape=Rectangle 0,0,200,90,4,4 | Extend Mutation 28 | Mutation=FillColor #Black# | StrokeWidth 0 29 | 30 | 31 | [Icon] 32 | Meter=Image 33 | ImageName=[#@]Assets/icon.png 34 | X=8 35 | Y=21 36 | W=48 37 | H=48 38 | Z=2 39 | 40 | 41 | [ClientMeter] 42 | Meter=String 43 | MeasureName=ClientProcess 44 | X=60 45 | Y=22 46 | Z=2 47 | AntiAlias=1 48 | FontSize=11 49 | FontFace=#PrimaryFont# 50 | FontColor=#White# 51 | Text="Client: %1" 52 | 53 | 54 | [GameMeter] 55 | Meter=String 56 | MeasureName=GameProcess 57 | X=60 58 | Y=38 59 | Z=2 60 | AntiAlias=1 61 | FontSize=11 62 | FontFace=#PrimaryFont# 63 | FontColor=#White# 64 | Text="Game: %1" 65 | 66 | 67 | [RPCMeter] 68 | Meter=String 69 | MeasureName=RPCScript 70 | X=60 71 | Y=52 72 | Z=2 73 | AntiAlias=1 74 | FontSize=11 75 | FontFace=#PrimaryFont# 76 | FontColor=#White# 77 | Text="RPC: %1" 78 | --------------------------------------------------------------------------------