1 module mastodon; 2 3 // import std.net.curl:HTTP, post, get, patch, del, No; 4 import std.net.curl; 5 import std.json:JSONValue, parseJSON; 6 import std.conv:to; 7 8 /// 9 ClientConfig createApp(in string url, in string clientName, in string scopes, in string redirectUris = "urn:ietf:wg:oauth:2.0:oob"){ 10 auto res = post(url ~ "/api/v1/apps", [ "client_name" : clientName, 11 "redirect_uris" : redirectUris, 12 "scope" : scopes]).parseJSON; 13 auto result = ClientConfig(); 14 result.url = url; 15 result.id = res["client_id"].str; 16 result.secret = res["client_secret"].str; 17 return result; 18 } 19 20 /// 21 JSONValue signIn(in string url, in string clientId, in string clientIdSeclet, in string email, in string password, in string scopes = "read write follow"){ 22 string[string] option = ["client_id" : clientId, 23 "client_secret" : clientIdSeclet, 24 "grant_type" : "password", 25 "username" : email, 26 "password" : password, 27 "scope" : scopes]; 28 auto response = post(url ~ "/oauth/token", option).parseJSON; 29 return response; 30 } 31 32 /// 33 JSONValue signIn(in JSONValue clientConfig, in string email, in string password){ 34 import std.algorithm:filter; 35 import std.array:array; 36 return signIn(clientConfig["url"].str.filter!(e => e!='\\').to!string, clientConfig["client_id"].str, clientConfig["client_secret"].str, email, password); 37 } 38 39 JSONValue signIn(in ClientConfig clientConfig, in string email, in string password){ 40 return signIn(clientConfig.url, clientConfig.id, clientConfig.secret, email, password); 41 } 42 43 /++ 44 +/ 45 struct ClientConfig { 46 public{ 47 this(in string url, in string id, in string secret){ 48 this.url = url; 49 this.id = id; 50 this.secret = secret; 51 } 52 this(JSONValue v){ 53 url = v["url"].str; 54 id = v["client_id"].str; 55 secret = v["client_secret"].str; 56 } 57 string url; 58 string id; 59 string secret; 60 }//public 61 62 private{ 63 }//private 64 }//struct ClientConfig 65 66 /++ 67 +/ 68 enum StreamingType { 69 User = "user", 70 Public = "public", 71 Hashta = "hashtag" 72 }//enum StreamingType 73 74 /++ 75 +/ 76 class Client { 77 private alias This = typeof(this); 78 /++ 79 +/ 80 private enum Method { 81 GET, POST, DELETE, PATCH 82 }//enum Method 83 public{ 84 this(in ClientConfig clientToken){ 85 _clientToken = clientToken; 86 } 87 88 This signIn(in string email, in string password){ 89 _userToken = _clientToken.signIn(email, password); 90 return this; 91 }; 92 93 JSONValue request(Method M, T)(in string endPoint, T arg = null){ 94 auto http = HTTP(_clientToken.url); 95 http.addRequestHeader("Authorization", "Bearer " ~ _userToken["access_token"].str); 96 // http.handle.set(CurlOption.ssl_verifypeer, false); 97 string url = _clientToken.url ~ endPoint; 98 JSONValue response; 99 100 static if(M == Method.GET){ 101 response = get(url, http).parseJSON; 102 } 103 static if(M == Method.POST){ 104 response = post(url, arg, http).parseJSON; 105 } 106 static if(M == Method.PATCH){ 107 response = patch(url, arg, http).parseJSON; 108 } 109 static if(M == Method.DELETE){ 110 del(url, http); 111 } 112 113 return response; 114 } 115 116 /// 117 JSONValue account(in uint id){ 118 return request!(Method.GET)("/api/v1/accounts/" ~ id.to!string); 119 } 120 121 JSONValue verifyAccountCredentials(){ 122 return request!(Method.GET)("/api/v1/accounts/verify_credentials"); 123 } 124 125 JSONValue updateAccountCredentials(in string arg){ 126 // TODO doesn't work 127 return request!(Method.PATCH)("/api/v1/accounts/update_credentials", arg); 128 } 129 130 /// 131 JSONValue accountFollowers(in uint id){ 132 return request!(Method.GET)("/api/v1/accounts/" ~ id.to!string ~ "/followers"); 133 } 134 135 /// 136 JSONValue accountFollowing(in uint id){ 137 return request!(Method.GET)("/api/v1/accounts/" ~ id.to!string ~ "/following"); 138 } 139 140 /// 141 JSONValue accountStatuses(in uint id){ 142 return request!(Method.GET)("/api/v1/accounts/" ~ id.to!string ~ "/statuses"); 143 } 144 145 /// 146 JSONValue followAccount(in uint id){ 147 return request!(Method.POST)("/api/v1/accounts/" ~ id.to!string ~ "/follow"); 148 } 149 150 /// 151 JSONValue unfollowAccount(in uint id){ 152 return request!(Method.POST)("/api/v1/accounts/" ~ id.to!string ~ "/unfollow"); 153 } 154 155 /// 156 // GET /api/v1/accounts/:id/block 157 JSONValue blockAccount(in uint id){ 158 return request!(Method.POST)("/api/v1/accounts/" ~ id.to!string ~ "/block"); 159 } 160 161 /// 162 JSONValue unblockAccount(in uint id){ 163 return request!(Method.POST)("/api/v1/accounts/" ~ id.to!string ~ "/unblock"); 164 } 165 166 /// 167 JSONValue muteAccount(in uint id){ 168 return request!(Method.POST)("/api/v1/accounts/" ~ id.to!string ~ "/mute"); 169 } 170 171 /// 172 JSONValue unmuteAccount(in uint id){ 173 return request!(Method.POST)("/api/v1/accounts/" ~ id.to!string ~ "/unmute"); 174 } 175 176 /// 177 JSONValue accountRelationships(in uint[] id...){ 178 return accountRelationships(id); 179 } 180 181 /// 182 JSONValue accountRelationships(in uint[] arr){ 183 import std.algorithm:map; 184 import std.string:join; 185 string qArray = arr.map!(e => "id[]=" ~ e.to!string).join("&"); 186 return request!(Method.GET)("/api/v1/accounts/relationships/?" ~ qArray); 187 } 188 189 /// 190 JSONValue searchAccount(in string q, in uint limit = 40){ 191 return request!(Method.GET)("/api/v1/accounts/search/?q="~q~"&limit"~limit.to!string); 192 } 193 194 /// 195 JSONValue blocks(){ 196 return request!(Method.GET)("/api/v1/blocks"); 197 } 198 199 /// 200 JSONValue favourites(){ 201 return request!(Method.GET)("/api/v1/favourites"); 202 } 203 204 /// 205 JSONValue followRequests(){ 206 return request!(Method.GET)("/api/v1/follow_requests"); 207 } 208 209 /// 210 JSONValue authorizeFollowRequest(in uint id){ 211 return request!(Method.POST)("/api/v1/follow_requests/authorize", ["id":id.to!string]); 212 } 213 214 /// 215 JSONValue rejectFollowRequest(in uint id){ 216 return request!(Method.POST)("/api/v1/follow_requests/reject", ["id":id.to!string]); 217 } 218 219 /// 220 JSONValue followRemoteUser(in string uri){ 221 return request!(Method.POST)("/api/v1/follows", ["uri":uri.to!string]); 222 } 223 224 /// 225 JSONValue instance(){ 226 return request!(Method.GET)("/api/v1/instance"); 227 } 228 229 /// TODO 230 // POST /api/v1/media 231 232 /// 233 JSONValue mutes(){ 234 return request!(Method.GET)("/api/v1/mutes"); 235 } 236 237 /// 238 JSONValue notifications(){ 239 return request!(Method.GET)("/api/v1/notifications"); 240 } 241 242 /// 243 JSONValue notifications(in uint id){ 244 return request!(Method.GET)("/api/v1/notifications/" ~ id.to!string); 245 } 246 247 /// 248 JSONValue clearNotifications(){ 249 return request!(Method.GET)("/api/v1/notifications/clear"); 250 } 251 252 /// 253 JSONValue reports(){ 254 return request!(Method.GET)("/api/v1/reports"); 255 } 256 257 /// TODO 258 // POST /api/v1/reports 259 260 /// TODO 261 // GET /api/v1/search 262 // JSONValue search(in string q, bool resolve = false){ 263 // import std.conv:to; 264 // string qArray = "q="~q ~ resolve?"&resolve":""; 265 // return request!(Method.GET)("/api/v1/search/?" ~ qArray); 266 // } 267 268 /// 269 JSONValue status(in uint id){ 270 return request!(Method.GET)("/api/v1/statuses/" ~ id.to!string); 271 } 272 273 /// 274 JSONValue statusContext(in uint id){ 275 return request!(Method.GET)("/api/v1/statuses/" ~ id.to!string ~ "/context"); 276 } 277 278 /// 279 JSONValue statusCard(in uint id){ 280 return request!(Method.GET)("/api/v1/statuses/" ~ id.to!string ~ "/card"); 281 } 282 283 /// 284 JSONValue rebloggedBy(in uint id){ 285 return request!(Method.GET)("/api/v1/statuses/" ~ id.to!string ~ "/reblogged_by"); 286 } 287 288 289 /// 290 JSONValue favouritedBy(in uint id){ 291 return request!(Method.GET)("/api/v1/statuses/" ~ id.to!string ~ "/favourited_by"); 292 } 293 294 /// TODO add params 295 JSONValue postStatus(in string status){ 296 string[string] arg = ["status" : status]; 297 return request!(Method.POST)("/api/v1/statuses", arg); 298 } 299 300 /// 301 JSONValue deleteStatus(in uint statusId){ 302 return request!(Method.DELETE)("/api/v1/statuses/"~statusId.to!string); 303 } 304 305 /// 306 JSONValue reblog(in uint statusId){ 307 return request!(Method.POST)("/api/v1/statuses/"~statusId.to!string~"/reblog"); 308 } 309 310 /// 311 JSONValue unreblog(in uint statusId){ 312 return request!(Method.POST)("/api/v1/statuses/"~statusId.to!string~"/unreblog"); 313 } 314 315 /// 316 JSONValue favourite(in uint statusId){ 317 return request!(Method.POST)("/api/v1/statuses/"~statusId.to!string~"/favourite"); 318 } 319 320 /// 321 JSONValue unfavourite(in uint statusId){ 322 return request!(Method.POST)("/api/v1/statuses/"~statusId.to!string~"/unfavourite"); 323 } 324 325 /// 326 JSONValue timelineHome(){ 327 return request!(Method.GET)("/api/v1/timelines/home"); 328 } 329 330 /// 331 JSONValue timelinePublic(){ 332 return request!(Method.GET)("/api/v1/timelines/public"); 333 } 334 335 /// 336 JSONValue timelineHashtag(in string tag){ 337 return request!(Method.GET)("/api/v1/timelines/tag/" ~ tag); 338 } 339 340 // auto stream(in StreamingType type){ 341 // import std.net.curl; 342 // auto http = HTTP(_clientToken.url); 343 // http.addRequestHeader("Authorization", "Bearer " ~ _userToken["access_token"].str); 344 // http.method = HTTP.Method.get; 345 // string url = _clientToken.url ~ "/api/v1/streaming/" ~ type; 346 // auto stream = byLineAsync( 347 // url, 348 // No.keepTerminator, 349 // '\x0a', 350 // 10, 351 // http 352 // ); 353 // return stream; 354 // } 355 }//public 356 357 private{ 358 ClientConfig _clientToken; 359 JSONValue _userToken; 360 }//private 361 }//class Client 362 363 // /++ 364 // +/ 365 // struct Account { 366 // // TODO 367 // }//struct Account 368 // 369 // /++ 370 // +/ 371 // struct Application { 372 // // TODO 373 // }//struct Application 374 // 375 // /++ 376 // +/ 377 // struct Attachment { 378 // // TODO 379 // }//struct Attachment 380 // 381 // /++ 382 // +/ 383 // struct Card { 384 // // TODO 385 // }//struct Card 386 // 387 // /++ 388 // +/ 389 // struct Context { 390 // // TODO 391 // }//struct Context 392 // 393 // /++ 394 // +/ 395 // struct Error { 396 // // TODO 397 // }//struct Error 398 // 399 // /++ 400 // +/ 401 // struct Instance { 402 // // TODO 403 // }//struct Instance 404 // 405 // /++ 406 // +/ 407 // struct Mention { 408 // // TODO 409 // }//struct Mention 410 // 411 // /++ 412 // +/ 413 // struct Notification { 414 // // TODO 415 // }//struct Notification 416 // 417 // /++ 418 // +/ 419 // struct Relationship { 420 // // TODO 421 // }//struct Relationship 422 // 423 // /++ 424 // +/ 425 // struct Report { 426 // // TODO 427 // }//struct Report 428 // 429 // /++ 430 // +/ 431 // struct Result { 432 // // TODO 433 // }//struct Result 434 // 435 // /++ 436 // +/ 437 // struct Status { 438 // // TODO 439 // }//struct Status 440 // 441 // /++ 442 // +/ 443 // struct Tag { 444 // // TODO 445 // }//struct Tag 446