1+ using Newtonsoft . Json ;
2+ using Oxide . Core . Libraries ;
3+ using Oxide . Core . Libraries . Covalence ;
4+ using System ;
5+ using System . Collections . Generic ;
6+ using System . Linq ;
7+
8+ namespace Oxide . Plugins ;
9+
10+ [ Info ( "AzLink" , "Azuriom" , AzLinkVersion ) ]
11+ [ Description ( "Link your Azuriom website with an Oxide server." ) ]
12+ class AzLink : CovalencePlugin
13+ {
14+ private const string AzLinkVersion = "0.1.0" ;
15+
16+ private DateTime lastSent = DateTime . MinValue ;
17+ private DateTime lastFullSent = DateTime . MinValue ;
18+
19+ private void Init ( )
20+ {
21+ timer . Every ( 60 , TryFetch ) ;
22+ }
23+
24+ protected override void LoadDefaultConfig ( )
25+ {
26+ Log ( "Creating a new configuration file." ) ;
27+
28+ Config [ "URL" ] = null ;
29+ Config [ "SiteKey" ] = null ;
30+ }
31+
32+ [ Command ( "azlink.setup" ) , Permission ( "azlink.setup" ) ]
33+ private void SetupCommand ( IPlayer player , string command , string [ ] args )
34+ {
35+ if ( args . Length < 2 )
36+ {
37+ player . Reply (
38+ "You must first add this server in your Azuriom admin dashboard, in the 'Servers' section." ) ;
39+ return ;
40+ }
41+
42+ Config [ "URL" ] = args [ 0 ] ;
43+ Config [ "SiteKey" ] = args [ 1 ] ;
44+
45+ PingWebsite ( ( ) =>
46+ {
47+ player . Reply ( "Linked to the website successfully." ) ;
48+ SaveConfig ( ) ;
49+ } , code => player . Reply ( $ "An error occurred, code { code } ") ) ;
50+ }
51+
52+ [ Command ( "azlink.status" ) , Permission ( "azlink.status" ) ]
53+ private void StatusCommand ( IPlayer player , string command , string [ ] args )
54+ {
55+ if ( Config [ "URL" ] == null )
56+ {
57+ player . Reply ( "AzLink is not configured yet, use the 'setup' subcommand first." ) ;
58+ return ;
59+ }
60+
61+ PingWebsite ( ( ) => player . Reply ( "Connected to the website successfully." ) ,
62+ code => player . Reply ( $ "An error occurred, code { code } ") ) ;
63+
64+ player . Reply ( "Connected to the website successfully." ) ;
65+ }
66+
67+ [ Command ( "azlink.fetch" ) , Permission ( "azlink.fetch" ) ]
68+ private void FetchCommand ( IPlayer player , string command , string [ ] args )
69+ {
70+ if ( Config [ "URL" ] == null )
71+ {
72+ player . Reply ( "AzLink is not configured yet, use the 'setup' subcommand first." ) ;
73+ return ;
74+ }
75+
76+ RunFetch ( res =>
77+ {
78+ DispatchCommands ( res . Commands ) ;
79+
80+ player . Reply ( "Data has been fetched successfully." ) ;
81+ } , code => player . Reply ( $ "An error occurred, code { code } ") , true ) ;
82+ }
83+
84+ private void TryFetch ( )
85+ {
86+ var url = ( string ? ) Config [ "URL" ] ;
87+ var siteKey = ( string ? ) Config [ "SiteKey" ] ;
88+ var now = DateTime . Now ;
89+
90+ if ( url == null || siteKey == null )
91+ {
92+ return ;
93+ }
94+
95+ if ( ( now - lastSent ) . Seconds < 15 )
96+ {
97+ return ;
98+ }
99+
100+ lastSent = now ;
101+
102+ var sendFull = now . Minute % 15 == 0 && ( now - lastFullSent ) . Seconds > 60 ;
103+
104+ if ( sendFull )
105+ {
106+ lastFullSent = now ;
107+ }
108+
109+ RunFetch ( res => DispatchCommands ( res . Commands ) ,
110+ code => LogError ( "Unable to send data to the website (code {0})" , code ) , sendFull ) ;
111+ }
112+
113+ private void RunFetch ( Action < FetchResponse > callback , Action < int > errorHandler , bool sendFullData )
114+ {
115+ var url = ( string ) Config [ "URL" ] ;
116+ var body = JsonConvert . SerializeObject ( GetServerData ( sendFullData ) ) ;
117+
118+ webrequest . Enqueue ( $ "{ url } /api/azlink", body , ( code , response ) =>
119+ {
120+ if ( code is < 200 or >= 300 )
121+ {
122+ errorHandler ( code ) ;
123+ return ;
124+ }
125+
126+ callback ( JsonConvert . DeserializeObject < FetchResponse > ( response ) ) ;
127+ } , this , RequestMethod . POST , GetRequestHeaders ( ) ) ;
128+ }
129+
130+ private void PingWebsite ( Action onSuccess , Action < int > errorHandler )
131+ {
132+ var url = ( string ? ) Config [ "URL" ] ;
133+ var siteKey = ( string ? ) Config [ "SiteKey" ] ;
134+
135+ if ( url == null || siteKey == null )
136+ {
137+ throw new ApplicationException ( "AzLink is not configured yet." ) ;
138+ }
139+
140+ webrequest . Enqueue ( $ "{ url } /api/azlink", null , ( code , _ ) =>
141+ {
142+ if ( code is < 200 or >= 300 )
143+ {
144+ errorHandler ( code ) ;
145+ return ;
146+ }
147+
148+ onSuccess ( ) ;
149+ } , this , RequestMethod . GET , GetRequestHeaders ( ) ) ;
150+ }
151+
152+ private void DispatchCommands ( ICollection < PendingCommand > commands )
153+ {
154+ if ( commands . Count == 0 )
155+ {
156+ return ;
157+ }
158+
159+ foreach ( var info in commands )
160+ {
161+ var player = players . FindPlayerById ( info . UserId ) ;
162+ var name = player ? . Name ?? info . UserName ;
163+
164+ foreach ( var command in info . Values )
165+ {
166+ var cmd = command . Replace ( "{player}" , name )
167+ . Replace ( "{steam_id}" , info . UserId ) ;
168+
169+ Log ( "Dispatching command to {0} ({1}): {2}" , name , info . UserId , cmd ) ;
170+
171+ server . Command ( cmd ) ;
172+ }
173+ }
174+
175+ Log ( "Dispatched commands to {0} players." , commands . Count ) ;
176+ }
177+
178+ private Dictionary < string , object > GetServerData ( bool includeFullData )
179+ {
180+ var online = players . Connected . Select ( player => new Dictionary < string , string >
181+ {
182+ { "name" , player . Name } , { "uid" , player . Id }
183+ } ) ;
184+ var data = new Dictionary < string , object >
185+ {
186+ {
187+ "platform" , new Dictionary < string , string > ( )
188+ {
189+ { "type" , "OXIDE" } ,
190+ { "name" , $ "Oxide - { game } " } ,
191+ { "version" , server . Version } ,
192+ }
193+ } ,
194+ { "version" , AzLinkVersion } ,
195+ { "players" , online } ,
196+ { "maxPlayers" , server . MaxPlayers } ,
197+ { "full" , includeFullData }
198+ } ;
199+
200+ if ( includeFullData )
201+ {
202+ data . Add ( "ram" , GC . GetTotalMemory ( false ) / 1024 / 1024 ) ;
203+ }
204+
205+ return data ;
206+ }
207+
208+ private Dictionary < string , string > GetRequestHeaders ( )
209+ {
210+ return new Dictionary < string , string >
211+ {
212+ { "Azuriom-Link-Token" , ( string ) Config [ "SiteKey" ] } ,
213+ { "Accept" , "application/json" } ,
214+ { "Content-type" , "application/json" } ,
215+ { "User-Agent" , "AzLink Oxide v" + AzLinkVersion } ,
216+ } ;
217+ }
218+ }
219+
220+ class FetchResponse
221+ {
222+ [ JsonProperty ( "commands" ) ] public List < PendingCommand > Commands { get ; set ; }
223+ }
224+
225+ class PendingCommand
226+ {
227+ [ JsonProperty ( "uid" ) ] public string UserId { get ; set ; }
228+
229+ [ JsonProperty ( "name" ) ] public string UserName { get ; set ; }
230+
231+ [ JsonProperty ( "values" ) ] public List < string > Values { get ; set ; }
232+ }
0 commit comments