@f3liz/rescript-misskey-api 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/es6/src/Misskey.mjs +339 -3
- package/package.json +1 -1
- package/src/Misskey.res +331 -2
package/lib/es6/src/Misskey.mjs
CHANGED
|
@@ -71,8 +71,27 @@ async function request(client, endpoint, paramsOpt, param) {
|
|
|
71
71
|
};
|
|
72
72
|
} catch (raw_err) {
|
|
73
73
|
let err = Primitive_exceptions.internalToException(raw_err);
|
|
74
|
+
let errorData = err.data;
|
|
74
75
|
let jsExn = Stdlib_JsExn.fromException(err);
|
|
75
|
-
let
|
|
76
|
+
let baseMsg = jsExn !== undefined ? Stdlib_Option.getOr(Stdlib_JsExn.message(Primitive_option.valFromOption(jsExn)), "Unknown error") : "Unknown error";
|
|
77
|
+
let msg;
|
|
78
|
+
if (errorData !== undefined) {
|
|
79
|
+
let obj = Stdlib_JSON.Decode.object(errorData);
|
|
80
|
+
if (obj !== undefined) {
|
|
81
|
+
let errObj = Stdlib_Option.flatMap(obj["error"], Stdlib_JSON.Decode.object);
|
|
82
|
+
if (errObj !== undefined) {
|
|
83
|
+
let code = Stdlib_Option.getOr(Stdlib_Option.flatMap(errObj["code"], Stdlib_JSON.Decode.string), "");
|
|
84
|
+
let message = Stdlib_Option.getOr(Stdlib_Option.flatMap(errObj["message"], Stdlib_JSON.Decode.string), "");
|
|
85
|
+
msg = baseMsg + ` [` + code + `] ` + message;
|
|
86
|
+
} else {
|
|
87
|
+
msg = baseMsg;
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
msg = baseMsg;
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
msg = baseMsg;
|
|
94
|
+
}
|
|
76
95
|
return {
|
|
77
96
|
TAG: "Error",
|
|
78
97
|
_0: msg
|
|
@@ -88,12 +107,16 @@ function origin(client) {
|
|
|
88
107
|
return client.origin;
|
|
89
108
|
}
|
|
90
109
|
|
|
110
|
+
function token(client) {
|
|
111
|
+
return client.token;
|
|
112
|
+
}
|
|
113
|
+
|
|
91
114
|
function close(client) {
|
|
92
115
|
Stdlib_Option.forEach(client.streamClient, StreamClient.close);
|
|
93
116
|
client.streamClient = undefined;
|
|
94
117
|
}
|
|
95
118
|
|
|
96
|
-
function create(client, text, visibilityOpt, cw, localOnlyOpt, replyId, renoteId, param) {
|
|
119
|
+
function create(client, text, visibilityOpt, cw, localOnlyOpt, replyId, renoteId, fileIds, param) {
|
|
97
120
|
let visibility = visibilityOpt !== undefined ? visibilityOpt : "public";
|
|
98
121
|
let localOnly = localOnlyOpt !== undefined ? localOnlyOpt : false;
|
|
99
122
|
let params = {};
|
|
@@ -114,6 +137,9 @@ function create(client, text, visibilityOpt, cw, localOnlyOpt, replyId, renoteId
|
|
|
114
137
|
Stdlib_Option.forEach(renoteId, v => {
|
|
115
138
|
params["renoteId"] = v;
|
|
116
139
|
});
|
|
140
|
+
Stdlib_Option.forEach(fileIds, ids => {
|
|
141
|
+
params["fileIds"] = ids.map(prim => prim);
|
|
142
|
+
});
|
|
117
143
|
return request(client, "notes/create", params, undefined);
|
|
118
144
|
}
|
|
119
145
|
|
|
@@ -198,13 +224,83 @@ function unreact(client, noteId) {
|
|
|
198
224
|
return request(client, "notes/reactions/delete", params, undefined);
|
|
199
225
|
}
|
|
200
226
|
|
|
227
|
+
function show(client, noteId) {
|
|
228
|
+
let params = {};
|
|
229
|
+
params["noteId"] = noteId;
|
|
230
|
+
return request(client, "notes/show", params, undefined);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function children(client, noteId, limitOpt, sinceId, untilId, param) {
|
|
234
|
+
let limit = limitOpt !== undefined ? limitOpt : 30;
|
|
235
|
+
let params = {};
|
|
236
|
+
params["noteId"] = noteId;
|
|
237
|
+
params["limit"] = limit;
|
|
238
|
+
Stdlib_Option.forEach(sinceId, v => {
|
|
239
|
+
params["sinceId"] = v;
|
|
240
|
+
});
|
|
241
|
+
Stdlib_Option.forEach(untilId, v => {
|
|
242
|
+
params["untilId"] = v;
|
|
243
|
+
});
|
|
244
|
+
return request(client, "notes/children", params, undefined);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function conversation(client, noteId, limitOpt, param) {
|
|
248
|
+
let limit = limitOpt !== undefined ? limitOpt : 30;
|
|
249
|
+
let params = {};
|
|
250
|
+
params["noteId"] = noteId;
|
|
251
|
+
params["limit"] = limit;
|
|
252
|
+
return request(client, "notes/conversation", params, undefined);
|
|
253
|
+
}
|
|
254
|
+
|
|
201
255
|
let Notes = {
|
|
202
256
|
create: create,
|
|
203
257
|
$$delete: $$delete,
|
|
204
258
|
fetch: fetch,
|
|
205
259
|
timeline: fetch,
|
|
206
260
|
react: react,
|
|
207
|
-
unreact: unreact
|
|
261
|
+
unreact: unreact,
|
|
262
|
+
show: show,
|
|
263
|
+
children: children,
|
|
264
|
+
conversation: conversation
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
function show$1(client, userId, username, host, param) {
|
|
268
|
+
let params = {};
|
|
269
|
+
Stdlib_Option.forEach(userId, v => {
|
|
270
|
+
params["userId"] = v;
|
|
271
|
+
});
|
|
272
|
+
Stdlib_Option.forEach(username, v => {
|
|
273
|
+
params["username"] = v;
|
|
274
|
+
});
|
|
275
|
+
Stdlib_Option.forEach(host, v => {
|
|
276
|
+
params["host"] = v;
|
|
277
|
+
});
|
|
278
|
+
return request(client, "users/show", params, undefined);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function notes(client, userId, limitOpt, withRepliesOpt, withRenotesOpt, withFilesOpt, sinceId, untilId, param) {
|
|
282
|
+
let limit = limitOpt !== undefined ? limitOpt : 20;
|
|
283
|
+
let withReplies = withRepliesOpt !== undefined ? withRepliesOpt : false;
|
|
284
|
+
let withRenotes = withRenotesOpt !== undefined ? withRenotesOpt : true;
|
|
285
|
+
let withFiles = withFilesOpt !== undefined ? withFilesOpt : false;
|
|
286
|
+
let params = {};
|
|
287
|
+
params["userId"] = userId;
|
|
288
|
+
params["limit"] = limit;
|
|
289
|
+
params["withReplies"] = withReplies;
|
|
290
|
+
params["withRenotes"] = withRenotes;
|
|
291
|
+
params["withFiles"] = withFiles;
|
|
292
|
+
Stdlib_Option.forEach(sinceId, v => {
|
|
293
|
+
params["sinceId"] = v;
|
|
294
|
+
});
|
|
295
|
+
Stdlib_Option.forEach(untilId, v => {
|
|
296
|
+
params["untilId"] = v;
|
|
297
|
+
});
|
|
298
|
+
return request(client, "users/notes", params, undefined);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
let Users = {
|
|
302
|
+
show: show$1,
|
|
303
|
+
notes: notes
|
|
208
304
|
};
|
|
209
305
|
|
|
210
306
|
function ensureStream(client) {
|
|
@@ -614,6 +710,185 @@ function isPermissionDenied(error) {
|
|
|
614
710
|
return match === "PERMISSION_DENIED";
|
|
615
711
|
}
|
|
616
712
|
|
|
713
|
+
async function get(client) {
|
|
714
|
+
let json = await request(client, "meta", undefined, undefined);
|
|
715
|
+
if (json.TAG !== "Ok") {
|
|
716
|
+
return {
|
|
717
|
+
TAG: "Error",
|
|
718
|
+
_0: json._0
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
let obj = Stdlib_JSON.Decode.object(json._0);
|
|
722
|
+
if (obj !== undefined) {
|
|
723
|
+
return {
|
|
724
|
+
TAG: "Ok",
|
|
725
|
+
_0: {
|
|
726
|
+
swPublickey: Stdlib_Option.flatMap(obj["swPublickey"], Stdlib_JSON.Decode.string)
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
} else {
|
|
730
|
+
return {
|
|
731
|
+
TAG: "Error",
|
|
732
|
+
_0: "Invalid meta response"
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
let Meta = {
|
|
738
|
+
get: get
|
|
739
|
+
};
|
|
740
|
+
|
|
741
|
+
async function register(client, endpoint, auth, publickey, sendReadMessageOpt, param) {
|
|
742
|
+
let sendReadMessage = sendReadMessageOpt !== undefined ? sendReadMessageOpt : false;
|
|
743
|
+
let params = {};
|
|
744
|
+
params["endpoint"] = endpoint;
|
|
745
|
+
params["auth"] = auth;
|
|
746
|
+
params["publickey"] = publickey;
|
|
747
|
+
params["sendReadMessage"] = sendReadMessage;
|
|
748
|
+
let json = await request(client, "sw/register", params, undefined);
|
|
749
|
+
if (json.TAG !== "Ok") {
|
|
750
|
+
return {
|
|
751
|
+
TAG: "Error",
|
|
752
|
+
_0: json._0
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
let obj = Stdlib_JSON.Decode.object(json._0);
|
|
756
|
+
if (obj !== undefined) {
|
|
757
|
+
return {
|
|
758
|
+
TAG: "Ok",
|
|
759
|
+
_0: {
|
|
760
|
+
state: Stdlib_Option.flatMap(obj["state"], Stdlib_JSON.Decode.string),
|
|
761
|
+
key: Stdlib_Option.flatMap(obj["key"], Stdlib_JSON.Decode.string),
|
|
762
|
+
userId: Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["userId"], Stdlib_JSON.Decode.string), ""),
|
|
763
|
+
endpoint: Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["endpoint"], Stdlib_JSON.Decode.string), ""),
|
|
764
|
+
sendReadMessage: Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["sendReadMessage"], Stdlib_JSON.Decode.bool), false)
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
} else {
|
|
768
|
+
return {
|
|
769
|
+
TAG: "Error",
|
|
770
|
+
_0: "Invalid sw/register response"
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
async function unregister(client, endpoint) {
|
|
776
|
+
let params = {};
|
|
777
|
+
params["endpoint"] = endpoint;
|
|
778
|
+
let e = await request(client, "sw/unregister", params, undefined);
|
|
779
|
+
if (e.TAG === "Ok") {
|
|
780
|
+
return {
|
|
781
|
+
TAG: "Ok",
|
|
782
|
+
_0: undefined
|
|
783
|
+
};
|
|
784
|
+
} else {
|
|
785
|
+
return {
|
|
786
|
+
TAG: "Error",
|
|
787
|
+
_0: e._0
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
let Sw = {
|
|
793
|
+
register: register,
|
|
794
|
+
unregister: unregister
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
async function create$1(client, name, url, secret, on, param) {
|
|
798
|
+
let params = {};
|
|
799
|
+
params["name"] = name;
|
|
800
|
+
params["url"] = url;
|
|
801
|
+
params["secret"] = secret;
|
|
802
|
+
params["on"] = on.map(prim => prim);
|
|
803
|
+
let json = await request(client, "i/webhooks/create", params, undefined);
|
|
804
|
+
if (json.TAG !== "Ok") {
|
|
805
|
+
return {
|
|
806
|
+
TAG: "Error",
|
|
807
|
+
_0: json._0
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
let obj = Stdlib_JSON.Decode.object(json._0);
|
|
811
|
+
if (obj === undefined) {
|
|
812
|
+
return {
|
|
813
|
+
TAG: "Error",
|
|
814
|
+
_0: "Invalid webhook response"
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
let id = Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["id"], Stdlib_JSON.Decode.string), "");
|
|
818
|
+
let name$1 = Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["name"], Stdlib_JSON.Decode.string), "");
|
|
819
|
+
let url$1 = Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["url"], Stdlib_JSON.Decode.string), "");
|
|
820
|
+
let active = Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["active"], Stdlib_JSON.Decode.bool), false);
|
|
821
|
+
return {
|
|
822
|
+
TAG: "Ok",
|
|
823
|
+
_0: {
|
|
824
|
+
id: id,
|
|
825
|
+
name: name$1,
|
|
826
|
+
url: url$1,
|
|
827
|
+
active: active
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
async function $$delete$1(client, webhookId) {
|
|
833
|
+
let params = {};
|
|
834
|
+
params["webhookId"] = webhookId;
|
|
835
|
+
let e = await request(client, "i/webhooks/delete", params, undefined);
|
|
836
|
+
if (e.TAG === "Ok") {
|
|
837
|
+
return {
|
|
838
|
+
TAG: "Ok",
|
|
839
|
+
_0: undefined
|
|
840
|
+
};
|
|
841
|
+
} else {
|
|
842
|
+
return {
|
|
843
|
+
TAG: "Error",
|
|
844
|
+
_0: e._0
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
async function list$1(client) {
|
|
850
|
+
let json = await request(client, "i/webhooks/list", undefined, undefined);
|
|
851
|
+
if (json.TAG !== "Ok") {
|
|
852
|
+
return {
|
|
853
|
+
TAG: "Error",
|
|
854
|
+
_0: json._0
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
let arr = Stdlib_JSON.Decode.array(json._0);
|
|
858
|
+
if (arr !== undefined) {
|
|
859
|
+
return {
|
|
860
|
+
TAG: "Ok",
|
|
861
|
+
_0: Stdlib_Array.filterMap(arr, item => {
|
|
862
|
+
let obj = Stdlib_JSON.Decode.object(item);
|
|
863
|
+
if (obj === undefined) {
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
let id = Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["id"], Stdlib_JSON.Decode.string), "");
|
|
867
|
+
let name = Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["name"], Stdlib_JSON.Decode.string), "");
|
|
868
|
+
let url = Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["url"], Stdlib_JSON.Decode.string), "");
|
|
869
|
+
let active = Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["active"], Stdlib_JSON.Decode.bool), false);
|
|
870
|
+
return {
|
|
871
|
+
id: id,
|
|
872
|
+
name: name,
|
|
873
|
+
url: url,
|
|
874
|
+
active: active
|
|
875
|
+
};
|
|
876
|
+
})
|
|
877
|
+
};
|
|
878
|
+
} else {
|
|
879
|
+
return {
|
|
880
|
+
TAG: "Ok",
|
|
881
|
+
_0: []
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
let Webhooks = {
|
|
887
|
+
create: create$1,
|
|
888
|
+
$$delete: $$delete$1,
|
|
889
|
+
list: list$1
|
|
890
|
+
};
|
|
891
|
+
|
|
617
892
|
function isAPIError(error) {
|
|
618
893
|
let obj = Stdlib_JSON.Decode.object(error);
|
|
619
894
|
if (obj === undefined) {
|
|
@@ -631,6 +906,61 @@ function isAPIError(error) {
|
|
|
631
906
|
}
|
|
632
907
|
}
|
|
633
908
|
|
|
909
|
+
async function upload(client, file, sensitiveOpt, param) {
|
|
910
|
+
let sensitive = sensitiveOpt !== undefined ? sensitiveOpt : false;
|
|
911
|
+
try {
|
|
912
|
+
let fd = (new FormData());
|
|
913
|
+
fd.append("file", file);
|
|
914
|
+
Stdlib_Option.forEach(client.token, tok => fd.append("i", tok));
|
|
915
|
+
if (sensitive) {
|
|
916
|
+
fd.append("isSensitive", "true");
|
|
917
|
+
}
|
|
918
|
+
let endpoint = client.origin + "/api/drive/files/create";
|
|
919
|
+
let json = await Ofetch.ofetch(endpoint, {
|
|
920
|
+
method: "POST",
|
|
921
|
+
body: fd
|
|
922
|
+
});
|
|
923
|
+
let idJson = Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), obj => obj["id"]);
|
|
924
|
+
if (idJson !== undefined) {
|
|
925
|
+
let id = Stdlib_JSON.Decode.string(idJson);
|
|
926
|
+
if (id !== undefined) {
|
|
927
|
+
return {
|
|
928
|
+
TAG: "Ok",
|
|
929
|
+
_0: id
|
|
930
|
+
};
|
|
931
|
+
} else {
|
|
932
|
+
return {
|
|
933
|
+
TAG: "Error",
|
|
934
|
+
_0: "Drive upload: id is not a string"
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
let errDetail = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), obj => obj["error"]), Stdlib_JSON.Decode.object), obj => obj["message"]), Stdlib_JSON.Decode.string);
|
|
939
|
+
let statusCode = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), obj => obj["error"]), Stdlib_JSON.Decode.object), obj => obj["code"]), Stdlib_JSON.Decode.string);
|
|
940
|
+
let msg = statusCode !== undefined ? (
|
|
941
|
+
errDetail !== undefined ? statusCode + ": " + errDetail : "Upload error: " + statusCode
|
|
942
|
+
) : (
|
|
943
|
+
errDetail !== undefined ? errDetail : "Unknown upload error"
|
|
944
|
+
);
|
|
945
|
+
return {
|
|
946
|
+
TAG: "Error",
|
|
947
|
+
_0: msg
|
|
948
|
+
};
|
|
949
|
+
} catch (raw_err) {
|
|
950
|
+
let err = Primitive_exceptions.internalToException(raw_err);
|
|
951
|
+
let jsExn = Stdlib_JsExn.fromException(err);
|
|
952
|
+
let msg$1 = jsExn !== undefined ? Stdlib_Option.getOr(Stdlib_JsExn.message(Primitive_option.valFromOption(jsExn)), "Drive upload failed") : "Drive upload failed";
|
|
953
|
+
return {
|
|
954
|
+
TAG: "Error",
|
|
955
|
+
_0: msg$1
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
let Drive = {
|
|
961
|
+
upload: upload
|
|
962
|
+
};
|
|
963
|
+
|
|
634
964
|
let $$default = connect;
|
|
635
965
|
|
|
636
966
|
export {
|
|
@@ -641,14 +971,20 @@ export {
|
|
|
641
971
|
request,
|
|
642
972
|
currentUser,
|
|
643
973
|
origin,
|
|
974
|
+
token,
|
|
644
975
|
close,
|
|
645
976
|
Notes,
|
|
977
|
+
Users,
|
|
646
978
|
Stream,
|
|
647
979
|
Emojis,
|
|
648
980
|
CustomTimelines,
|
|
649
981
|
MiAuth,
|
|
650
982
|
isPermissionDenied,
|
|
983
|
+
Meta,
|
|
984
|
+
Sw,
|
|
985
|
+
Webhooks,
|
|
651
986
|
isAPIError,
|
|
987
|
+
Drive,
|
|
652
988
|
$$default as default,
|
|
653
989
|
}
|
|
654
990
|
/* Not a pure module */
|
package/package.json
CHANGED
package/src/Misskey.res
CHANGED
|
@@ -117,10 +117,29 @@ let request = async (
|
|
|
117
117
|
Ok(json)
|
|
118
118
|
} catch {
|
|
119
119
|
| err =>
|
|
120
|
-
|
|
120
|
+
// ofetch attaches response data to error.data for non-2xx responses
|
|
121
|
+
let errorData: option<JSON.t> = (err->Obj.magic)["data"]
|
|
122
|
+
let baseMsg = switch err->JsExn.fromException {
|
|
121
123
|
| Some(jsExn) => JsExn.message(jsExn)->Option.getOr("Unknown error")
|
|
122
124
|
| None => "Unknown error"
|
|
123
125
|
}
|
|
126
|
+
let msg = switch errorData {
|
|
127
|
+
| Some(data) =>
|
|
128
|
+
switch data->JSON.Decode.object {
|
|
129
|
+
| Some(obj) =>
|
|
130
|
+
switch obj->Dict.get("error")->Option.flatMap(JSON.Decode.object) {
|
|
131
|
+
| Some(errObj) =>
|
|
132
|
+
let code =
|
|
133
|
+
errObj->Dict.get("code")->Option.flatMap(JSON.Decode.string)->Option.getOr("")
|
|
134
|
+
let message =
|
|
135
|
+
errObj->Dict.get("message")->Option.flatMap(JSON.Decode.string)->Option.getOr("")
|
|
136
|
+
`${baseMsg} [${code}] ${message}`
|
|
137
|
+
| None => baseMsg
|
|
138
|
+
}
|
|
139
|
+
| None => baseMsg
|
|
140
|
+
}
|
|
141
|
+
| None => baseMsg
|
|
142
|
+
}
|
|
124
143
|
Error(msg)
|
|
125
144
|
}
|
|
126
145
|
}
|
|
@@ -133,6 +152,9 @@ let currentUser = (client: t): promise<result<JSON.t, string>> => {
|
|
|
133
152
|
/// Get client origin (instance URL).
|
|
134
153
|
let origin = (client: t): string => client.origin
|
|
135
154
|
|
|
155
|
+
/// Get client authentication token.
|
|
156
|
+
let token = (client: t): option<string> => client.token
|
|
157
|
+
|
|
136
158
|
/// Close client and cleanup (close streaming connections).
|
|
137
159
|
let close = (client: t): unit => {
|
|
138
160
|
client.streamClient->Option.forEach(s => s->StreamClient.close)
|
|
@@ -165,6 +187,7 @@ module Notes = {
|
|
|
165
187
|
~localOnly: bool=false,
|
|
166
188
|
~replyId: option<string>=?,
|
|
167
189
|
~renoteId: option<string>=?,
|
|
190
|
+
~fileIds: option<array<string>>=?,
|
|
168
191
|
(),
|
|
169
192
|
): promise<result<JSON.t, string>> => {
|
|
170
193
|
let params = Dict.make()
|
|
@@ -182,6 +205,9 @@ module Notes = {
|
|
|
182
205
|
cw->Option.forEach(v => params->Dict.set("cw", v->JSON.Encode.string))
|
|
183
206
|
replyId->Option.forEach(v => params->Dict.set("replyId", v->JSON.Encode.string))
|
|
184
207
|
renoteId->Option.forEach(v => params->Dict.set("renoteId", v->JSON.Encode.string))
|
|
208
|
+
fileIds->Option.forEach(ids =>
|
|
209
|
+
params->Dict.set("fileIds", ids->Array.map(JSON.Encode.string)->JSON.Encode.array)
|
|
210
|
+
)
|
|
185
211
|
|
|
186
212
|
request(client, "notes/create", ~params=params->JSON.Encode.object, ())
|
|
187
213
|
}
|
|
@@ -257,12 +283,89 @@ module Notes = {
|
|
|
257
283
|
params->Dict.set("noteId", noteId->JSON.Encode.string)
|
|
258
284
|
request(client, "notes/reactions/delete", ~params=params->JSON.Encode.object, ())
|
|
259
285
|
}
|
|
286
|
+
|
|
287
|
+
/// Get a single note by ID.
|
|
288
|
+
let show = (client: t, noteId: string): promise<result<JSON.t, string>> => {
|
|
289
|
+
let params = Dict.make()
|
|
290
|
+
params->Dict.set("noteId", noteId->JSON.Encode.string)
|
|
291
|
+
request(client, "notes/show", ~params=params->JSON.Encode.object, ())
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/// Get replies/children of a note.
|
|
295
|
+
let children = (
|
|
296
|
+
client: t,
|
|
297
|
+
noteId: string,
|
|
298
|
+
~limit: int=30,
|
|
299
|
+
~sinceId: option<string>=?,
|
|
300
|
+
~untilId: option<string>=?,
|
|
301
|
+
(),
|
|
302
|
+
): promise<result<JSON.t, string>> => {
|
|
303
|
+
let params = Dict.make()
|
|
304
|
+
params->Dict.set("noteId", noteId->JSON.Encode.string)
|
|
305
|
+
params->Dict.set("limit", limit->JSON.Encode.int)
|
|
306
|
+
sinceId->Option.forEach(v => params->Dict.set("sinceId", v->JSON.Encode.string))
|
|
307
|
+
untilId->Option.forEach(v => params->Dict.set("untilId", v->JSON.Encode.string))
|
|
308
|
+
request(client, "notes/children", ~params=params->JSON.Encode.object, ())
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/// Get the conversation thread (parent notes) for a note.
|
|
312
|
+
let conversation = (
|
|
313
|
+
client: t,
|
|
314
|
+
noteId: string,
|
|
315
|
+
~limit: int=30,
|
|
316
|
+
(),
|
|
317
|
+
): promise<result<JSON.t, string>> => {
|
|
318
|
+
let params = Dict.make()
|
|
319
|
+
params->Dict.set("noteId", noteId->JSON.Encode.string)
|
|
320
|
+
params->Dict.set("limit", limit->JSON.Encode.int)
|
|
321
|
+
request(client, "notes/conversation", ~params=params->JSON.Encode.object, ())
|
|
322
|
+
}
|
|
260
323
|
}
|
|
261
324
|
|
|
262
325
|
// ============================================================================
|
|
263
|
-
//
|
|
326
|
+
// Users API
|
|
264
327
|
// ============================================================================
|
|
265
328
|
|
|
329
|
+
module Users = {
|
|
330
|
+
/// Get user profile by username (and optional host for remote users).
|
|
331
|
+
let show = (
|
|
332
|
+
client: t,
|
|
333
|
+
~userId: option<string>=?,
|
|
334
|
+
~username: option<string>=?,
|
|
335
|
+
~host: option<string>=?,
|
|
336
|
+
(),
|
|
337
|
+
): promise<result<JSON.t, string>> => {
|
|
338
|
+
let params = Dict.make()
|
|
339
|
+
userId->Option.forEach(v => params->Dict.set("userId", v->JSON.Encode.string))
|
|
340
|
+
username->Option.forEach(v => params->Dict.set("username", v->JSON.Encode.string))
|
|
341
|
+
host->Option.forEach(v => params->Dict.set("host", v->JSON.Encode.string))
|
|
342
|
+
request(client, "users/show", ~params=params->JSON.Encode.object, ())
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/// Get notes posted by a user.
|
|
346
|
+
let notes = (
|
|
347
|
+
client: t,
|
|
348
|
+
userId: string,
|
|
349
|
+
~limit: int=20,
|
|
350
|
+
~withReplies: bool=false,
|
|
351
|
+
~withRenotes: bool=true,
|
|
352
|
+
~withFiles: bool=false,
|
|
353
|
+
~sinceId: option<string>=?,
|
|
354
|
+
~untilId: option<string>=?,
|
|
355
|
+
(),
|
|
356
|
+
): promise<result<JSON.t, string>> => {
|
|
357
|
+
let params = Dict.make()
|
|
358
|
+
params->Dict.set("userId", userId->JSON.Encode.string)
|
|
359
|
+
params->Dict.set("limit", limit->JSON.Encode.int)
|
|
360
|
+
params->Dict.set("withReplies", withReplies->JSON.Encode.bool)
|
|
361
|
+
params->Dict.set("withRenotes", withRenotes->JSON.Encode.bool)
|
|
362
|
+
params->Dict.set("withFiles", withFiles->JSON.Encode.bool)
|
|
363
|
+
sinceId->Option.forEach(v => params->Dict.set("sinceId", v->JSON.Encode.string))
|
|
364
|
+
untilId->Option.forEach(v => params->Dict.set("untilId", v->JSON.Encode.string))
|
|
365
|
+
request(client, "users/notes", ~params=params->JSON.Encode.object, ())
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
266
369
|
module Stream = {
|
|
267
370
|
type subscription = {dispose: unit => unit}
|
|
268
371
|
|
|
@@ -660,6 +763,166 @@ let isPermissionDenied = (error: JSON.t): bool => {
|
|
|
660
763
|
}
|
|
661
764
|
|
|
662
765
|
/// Check if error is an API error and extract error info.
|
|
766
|
+
// ============================================================================
|
|
767
|
+
// Instance Meta API
|
|
768
|
+
// ============================================================================
|
|
769
|
+
|
|
770
|
+
module Meta = {
|
|
771
|
+
type meta = {
|
|
772
|
+
swPublickey: option<string>,
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/// Get instance metadata (includes VAPID public key for push notifications).
|
|
776
|
+
let get = async (client: t): result<meta, string> => {
|
|
777
|
+
switch await request(client, "meta", ()) {
|
|
778
|
+
| Ok(json) =>
|
|
779
|
+
switch json->JSON.Decode.object {
|
|
780
|
+
| Some(obj) =>
|
|
781
|
+
Ok({
|
|
782
|
+
swPublickey: obj
|
|
783
|
+
->Dict.get("swPublickey")
|
|
784
|
+
->Option.flatMap(JSON.Decode.string),
|
|
785
|
+
})
|
|
786
|
+
| None => Error("Invalid meta response")
|
|
787
|
+
}
|
|
788
|
+
| Error(e) => Error(e)
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// ============================================================================
|
|
794
|
+
// Service Worker (Push Notification) API
|
|
795
|
+
// ============================================================================
|
|
796
|
+
|
|
797
|
+
module Sw = {
|
|
798
|
+
type registration = {
|
|
799
|
+
state: option<string>,
|
|
800
|
+
key: option<string>,
|
|
801
|
+
userId: string,
|
|
802
|
+
endpoint: string,
|
|
803
|
+
sendReadMessage: bool,
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/// Register a push notification endpoint with the Misskey instance.
|
|
807
|
+
let register = async (
|
|
808
|
+
client: t,
|
|
809
|
+
~endpoint: string,
|
|
810
|
+
~auth: string,
|
|
811
|
+
~publickey: string,
|
|
812
|
+
~sendReadMessage: bool=false,
|
|
813
|
+
(),
|
|
814
|
+
): result<registration, string> => {
|
|
815
|
+
let params = Dict.make()
|
|
816
|
+
params->Dict.set("endpoint", endpoint->JSON.Encode.string)
|
|
817
|
+
params->Dict.set("auth", auth->JSON.Encode.string)
|
|
818
|
+
params->Dict.set("publickey", publickey->JSON.Encode.string)
|
|
819
|
+
params->Dict.set("sendReadMessage", sendReadMessage->JSON.Encode.bool)
|
|
820
|
+
switch await request(client, "sw/register", ~params=params->JSON.Encode.object, ()) {
|
|
821
|
+
| Ok(json) =>
|
|
822
|
+
switch json->JSON.Decode.object {
|
|
823
|
+
| Some(obj) =>
|
|
824
|
+
Ok({
|
|
825
|
+
state: obj->Dict.get("state")->Option.flatMap(JSON.Decode.string),
|
|
826
|
+
key: obj->Dict.get("key")->Option.flatMap(JSON.Decode.string),
|
|
827
|
+
userId: obj->Dict.get("userId")->Option.flatMap(JSON.Decode.string)->Option.getOr(""),
|
|
828
|
+
endpoint: obj->Dict.get("endpoint")->Option.flatMap(JSON.Decode.string)->Option.getOr(""),
|
|
829
|
+
sendReadMessage: obj
|
|
830
|
+
->Dict.get("sendReadMessage")
|
|
831
|
+
->Option.flatMap(JSON.Decode.bool)
|
|
832
|
+
->Option.getOr(false),
|
|
833
|
+
})
|
|
834
|
+
| None => Error("Invalid sw/register response")
|
|
835
|
+
}
|
|
836
|
+
| Error(e) => Error(e)
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/// Unregister a push notification endpoint.
|
|
841
|
+
let unregister = async (client: t, ~endpoint: string): result<unit, string> => {
|
|
842
|
+
let params = Dict.make()
|
|
843
|
+
params->Dict.set("endpoint", endpoint->JSON.Encode.string)
|
|
844
|
+
switch await request(client, "sw/unregister", ~params=params->JSON.Encode.object, ()) {
|
|
845
|
+
| Ok(_) => Ok()
|
|
846
|
+
| Error(e) => Error(e)
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// ============================================================================
|
|
852
|
+
// Webhooks API
|
|
853
|
+
// ============================================================================
|
|
854
|
+
|
|
855
|
+
module Webhooks = {
|
|
856
|
+
type webhook = {
|
|
857
|
+
id: string,
|
|
858
|
+
name: string,
|
|
859
|
+
url: string,
|
|
860
|
+
active: bool,
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/// Create a webhook on the Misskey instance.
|
|
864
|
+
let create = async (
|
|
865
|
+
client: t,
|
|
866
|
+
~name: string,
|
|
867
|
+
~url: string,
|
|
868
|
+
~secret: string,
|
|
869
|
+
~on: array<string>,
|
|
870
|
+
(),
|
|
871
|
+
): result<webhook, string> => {
|
|
872
|
+
let params = Dict.make()
|
|
873
|
+
params->Dict.set("name", name->JSON.Encode.string)
|
|
874
|
+
params->Dict.set("url", url->JSON.Encode.string)
|
|
875
|
+
params->Dict.set("secret", secret->JSON.Encode.string)
|
|
876
|
+
params->Dict.set("on", on->Array.map(JSON.Encode.string)->JSON.Encode.array)
|
|
877
|
+
switch await request(client, "i/webhooks/create", ~params=params->JSON.Encode.object, ()) {
|
|
878
|
+
| Ok(json) =>
|
|
879
|
+
switch json->JSON.Decode.object {
|
|
880
|
+
| Some(obj) =>
|
|
881
|
+
let id = obj->Dict.get("id")->Option.flatMap(JSON.Decode.string)->Option.getOr("")
|
|
882
|
+
let name = obj->Dict.get("name")->Option.flatMap(JSON.Decode.string)->Option.getOr("")
|
|
883
|
+
let url = obj->Dict.get("url")->Option.flatMap(JSON.Decode.string)->Option.getOr("")
|
|
884
|
+
let active = obj->Dict.get("active")->Option.flatMap(JSON.Decode.bool)->Option.getOr(false)
|
|
885
|
+
Ok({id, name, url, active})
|
|
886
|
+
| None => Error("Invalid webhook response")
|
|
887
|
+
}
|
|
888
|
+
| Error(e) => Error(e)
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/// Delete a webhook by ID.
|
|
893
|
+
let delete = async (client: t, ~webhookId: string): result<unit, string> => {
|
|
894
|
+
let params = Dict.make()
|
|
895
|
+
params->Dict.set("webhookId", webhookId->JSON.Encode.string)
|
|
896
|
+
switch await request(client, "i/webhooks/delete", ~params=params->JSON.Encode.object, ()) {
|
|
897
|
+
| Ok(_) => Ok()
|
|
898
|
+
| Error(e) => Error(e)
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/// List webhooks for the current user.
|
|
903
|
+
let list = async (client: t): result<array<webhook>, string> => {
|
|
904
|
+
switch await request(client, "i/webhooks/list", ()) {
|
|
905
|
+
| Ok(json) =>
|
|
906
|
+
switch json->JSON.Decode.array {
|
|
907
|
+
| Some(arr) =>
|
|
908
|
+
Ok(arr->Array.filterMap(item => {
|
|
909
|
+
switch item->JSON.Decode.object {
|
|
910
|
+
| Some(obj) =>
|
|
911
|
+
let id = obj->Dict.get("id")->Option.flatMap(JSON.Decode.string)->Option.getOr("")
|
|
912
|
+
let name = obj->Dict.get("name")->Option.flatMap(JSON.Decode.string)->Option.getOr("")
|
|
913
|
+
let url = obj->Dict.get("url")->Option.flatMap(JSON.Decode.string)->Option.getOr("")
|
|
914
|
+
let active = obj->Dict.get("active")->Option.flatMap(JSON.Decode.bool)->Option.getOr(false)
|
|
915
|
+
Some({id, name, url, active})
|
|
916
|
+
| None => None
|
|
917
|
+
}
|
|
918
|
+
}))
|
|
919
|
+
| None => Ok([])
|
|
920
|
+
}
|
|
921
|
+
| Error(e) => Error(e)
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
663
926
|
let isAPIError = (error: JSON.t): option<apiError> => {
|
|
664
927
|
switch error->JSON.Decode.object {
|
|
665
928
|
| Some(obj) =>
|
|
@@ -675,4 +938,70 @@ let isAPIError = (error: JSON.t): option<apiError> => {
|
|
|
675
938
|
}
|
|
676
939
|
}
|
|
677
940
|
|
|
941
|
+
// ============================================================================
|
|
942
|
+
// Drive API
|
|
943
|
+
// ============================================================================
|
|
944
|
+
// NOTE: All HTTP calls in this module (and throughout Misskey.res) should use
|
|
945
|
+
// `Ofetch.ofetch` rather than raw `fetch`. ofetch handles JSON parsing,
|
|
946
|
+
// non-2xx error throwing, and runtime FormData detection automatically, which
|
|
947
|
+
// avoids the pitfall of calling `response.json()` on non-standard objects.
|
|
948
|
+
|
|
949
|
+
module Drive = {
|
|
950
|
+
/// Upload a File object to the Misskey drive.
|
|
951
|
+
/// Returns the drive file ID on success.
|
|
952
|
+
let upload = async (client: t, ~file: {..}, ~sensitive: bool=false, ()): result<string, string> => {
|
|
953
|
+
try {
|
|
954
|
+
let fd: {..} = %raw(`new FormData()`)
|
|
955
|
+
fd["append"]("file", file)
|
|
956
|
+
client.token->Option.forEach(tok => fd["append"]("i", tok))
|
|
957
|
+
if sensitive { fd["append"]("isSensitive", "true") }
|
|
958
|
+
|
|
959
|
+
let endpoint = client.origin ++ "/api/drive/files/create"
|
|
960
|
+
// Cast FormData to JSON.t so ofetch can accept it;
|
|
961
|
+
// ofetch detects FormData at runtime and sends multipart/form-data correctly.
|
|
962
|
+
let json = await Ofetch.ofetch(endpoint, {
|
|
963
|
+
method: "POST",
|
|
964
|
+
body: fd->Obj.magic,
|
|
965
|
+
})
|
|
966
|
+
|
|
967
|
+
switch json->JSON.Decode.object->Option.flatMap(obj => obj->Dict.get("id")) {
|
|
968
|
+
| Some(idJson) =>
|
|
969
|
+
switch idJson->JSON.Decode.string {
|
|
970
|
+
| Some(id) => Ok(id)
|
|
971
|
+
| None => Error("Drive upload: id is not a string")
|
|
972
|
+
}
|
|
973
|
+
| None =>
|
|
974
|
+
let errDetail =
|
|
975
|
+
json
|
|
976
|
+
->JSON.Decode.object
|
|
977
|
+
->Option.flatMap(obj => obj->Dict.get("error"))
|
|
978
|
+
->Option.flatMap(JSON.Decode.object)
|
|
979
|
+
->Option.flatMap(obj => obj->Dict.get("message"))
|
|
980
|
+
->Option.flatMap(JSON.Decode.string)
|
|
981
|
+
let statusCode =
|
|
982
|
+
json
|
|
983
|
+
->JSON.Decode.object
|
|
984
|
+
->Option.flatMap(obj => obj->Dict.get("error"))
|
|
985
|
+
->Option.flatMap(JSON.Decode.object)
|
|
986
|
+
->Option.flatMap(obj => obj->Dict.get("code"))
|
|
987
|
+
->Option.flatMap(JSON.Decode.string)
|
|
988
|
+
let msg = switch (statusCode, errDetail) {
|
|
989
|
+
| (Some(code), Some(detail)) => code ++ ": " ++ detail
|
|
990
|
+
| (None, Some(detail)) => detail
|
|
991
|
+
| (Some(code), None) => "Upload error: " ++ code
|
|
992
|
+
| (None, None) => "Unknown upload error"
|
|
993
|
+
}
|
|
994
|
+
Error(msg)
|
|
995
|
+
}
|
|
996
|
+
} catch {
|
|
997
|
+
| err =>
|
|
998
|
+
let msg = switch err->JsExn.fromException {
|
|
999
|
+
| Some(jsExn) => JsExn.message(jsExn)->Option.getOr("Drive upload failed")
|
|
1000
|
+
| None => "Drive upload failed"
|
|
1001
|
+
}
|
|
1002
|
+
Error(msg)
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
678
1007
|
let default = connect
|