sidechat.js 2.6.6 → 2.7.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/dist/classes/SidechatAPIClient.d.ts +166 -11
- package/dist/classes/SidechatAPIClient.d.ts.map +1 -1
- package/dist/index.cjs +853 -311
- package/dist/index.mjs +853 -311
- package/dist/types/SidechatTypes.d.ts +260 -9
- package/dist/types/SidechatTypes.d.ts.map +1 -1
- package/docs/sidechat.js/2.7.0/SidechatAPIClient.html +3 -0
- package/docs/sidechat.js/2.7.0/SidechatAPIError.html +3 -0
- package/docs/sidechat.js/2.7.0/SidechatColorList.html +3 -0
- package/docs/sidechat.js/2.7.0/classes_SidechatAPIClient.js.html +1897 -0
- package/docs/sidechat.js/2.7.0/classes_SidechatAPIError.js.html +16 -0
- package/docs/sidechat.js/2.7.0/classes_SidechatColorList.js.html +79 -0
- package/docs/sidechat.js/2.7.0/data/search.json +1 -0
- package/docs/sidechat.js/2.7.0/fonts/Inconsolata-Regular.ttf +0 -0
- package/docs/sidechat.js/2.7.0/fonts/OpenSans-Regular.ttf +0 -0
- package/docs/sidechat.js/2.7.0/fonts/WorkSans-Bold.ttf +0 -0
- package/docs/sidechat.js/2.7.0/global.html +3 -0
- package/docs/sidechat.js/2.7.0/index.html +4 -0
- package/docs/sidechat.js/2.7.0/scripts/core.js +726 -0
- package/docs/sidechat.js/2.7.0/scripts/core.min.js +23 -0
- package/docs/sidechat.js/2.7.0/scripts/resize.js +90 -0
- package/docs/sidechat.js/2.7.0/scripts/search.js +265 -0
- package/docs/sidechat.js/2.7.0/scripts/search.min.js +6 -0
- package/docs/sidechat.js/2.7.0/scripts/third-party/Apache-License-2.0.txt +202 -0
- package/docs/sidechat.js/2.7.0/scripts/third-party/fuse.js +9 -0
- package/docs/sidechat.js/2.7.0/scripts/third-party/hljs-line-num-original.js +369 -0
- package/docs/sidechat.js/2.7.0/scripts/third-party/hljs-line-num.js +1 -0
- package/docs/sidechat.js/2.7.0/scripts/third-party/hljs-original.js +5171 -0
- package/docs/sidechat.js/2.7.0/scripts/third-party/hljs.js +1 -0
- package/docs/sidechat.js/2.7.0/scripts/third-party/popper.js +5 -0
- package/docs/sidechat.js/2.7.0/scripts/third-party/tippy.js +1 -0
- package/docs/sidechat.js/2.7.0/scripts/third-party/tocbot.js +672 -0
- package/docs/sidechat.js/2.7.0/scripts/third-party/tocbot.min.js +1 -0
- package/docs/sidechat.js/2.7.0/styles/clean-jsdoc-theme-base.css +1159 -0
- package/docs/sidechat.js/2.7.0/styles/clean-jsdoc-theme-dark.css +412 -0
- package/docs/sidechat.js/2.7.0/styles/clean-jsdoc-theme-light.css +482 -0
- package/docs/sidechat.js/2.7.0/styles/clean-jsdoc-theme-scrollbar.css +30 -0
- package/docs/sidechat.js/2.7.0/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
- package/docs/sidechat.js/2.7.0/styles/clean-jsdoc-theme.min.css +1 -0
- package/docs/sidechat.js/2.7.0/tutorial-Authentication.html +16 -0
- package/docs/sidechat.js/2.7.0/tutorial-Email Registration.html +6 -0
- package/docs/sidechat.js/2.7.0/tutorial-Feeds and Pagination.html +10 -0
- package/docs/sidechat.js/2.7.0/tutorial-Posting and Media.html +19 -0
- package/docs/sidechat.js/2.7.0/types_SidechatTypes.js.html +297 -0
- package/docs/tutorials/Authentication.md +63 -52
- package/docs/tutorials/Email Registration.md +19 -16
- package/docs/tutorials/Feeds and Pagination.md +45 -0
- package/docs/tutorials/Posting and Media.md +63 -0
- package/package.json +55 -55
- package/src/classes/SidechatAPIClient.js +477 -27
- package/src/types/SidechatTypes.js +294 -202
|
@@ -67,6 +67,16 @@ class SidechatAPIClient {
|
|
|
67
67
|
this.apiRoot = url;
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Set an identifier for this device, which will be sent with every request in the `X-Device-ID` header. Should be stable across sessions on the same device.
|
|
72
|
+
* @method
|
|
73
|
+
* @param {String} deviceIdentifier - stable identifier for this device
|
|
74
|
+
* @since 2.7.0
|
|
75
|
+
*/
|
|
76
|
+
setDeviceIdentifier = (deviceIdentifier) => {
|
|
77
|
+
this.defaultHeaders["X-Device-ID"] = deviceIdentifier;
|
|
78
|
+
};
|
|
79
|
+
|
|
70
80
|
/**
|
|
71
81
|
* Run an arbitrary API request using the current client's authentication
|
|
72
82
|
* @method
|
|
@@ -97,19 +107,32 @@ class SidechatAPIClient {
|
|
|
97
107
|
};
|
|
98
108
|
|
|
99
109
|
/**
|
|
100
|
-
* Initiate the login process with a phone number. Should be followed up with verifySMSCode().
|
|
110
|
+
* Initiate the login process with a phone number. Should be followed up with verifySMSCode(). If the response has an `error_code` of `"captcha_required"`, the server wants a captcha solved before it will send a code, and the request should be repeated with a captcha response. I haven't been able to recreate this yet, so I honestly don't know what captcha provider they want.
|
|
101
111
|
* @method
|
|
102
112
|
* @since 1.0.0
|
|
103
113
|
* @param {Number} phoneNumber - US phone number (WITHOUT +1) to send verification code to
|
|
114
|
+
* @param {Boolean} [useFallback] - send the code through the backup SMS route, which the official app offers when a code doesn't arrive
|
|
115
|
+
* @param {String} [captchaMethod] - type of captcha being answered
|
|
116
|
+
* @param {String} [captchaResponse] - solved captcha token
|
|
104
117
|
*/
|
|
105
|
-
loginViaSMS = async (
|
|
118
|
+
loginViaSMS = async (
|
|
119
|
+
phoneNumber,
|
|
120
|
+
useFallback = false,
|
|
121
|
+
captchaMethod = undefined,
|
|
122
|
+
captchaResponse = undefined,
|
|
123
|
+
) => {
|
|
106
124
|
try {
|
|
107
|
-
const
|
|
125
|
+
const endpoint = useFallback
|
|
126
|
+
? "/v1/login_register_sms_fallback"
|
|
127
|
+
: "/v1/login_register";
|
|
128
|
+
const res = await fetch(`${this.apiRoot}${endpoint}`, {
|
|
108
129
|
method: "POST",
|
|
109
130
|
headers: this.defaultHeaders,
|
|
110
131
|
body: JSON.stringify({
|
|
111
132
|
phone_number: `+1${phoneNumber}`,
|
|
112
133
|
version: 3,
|
|
134
|
+
captcha_method: captchaMethod,
|
|
135
|
+
captcha_response: captchaResponse,
|
|
113
136
|
}),
|
|
114
137
|
});
|
|
115
138
|
const json = await res.json();
|
|
@@ -154,8 +177,15 @@ class SidechatAPIClient {
|
|
|
154
177
|
* @since 1.0.0
|
|
155
178
|
* @param {Number} age - user's age in years
|
|
156
179
|
* @param {String} registrationID - the registration ID generated by verifySMSCode()
|
|
180
|
+
* @param {String} [timeZone] - time zone of the user (e.g. "America/Chicago"). Falls back to the system time zone.
|
|
181
|
+
* @param {String} [inviteLinkURL] - group invite link that the user signed up through
|
|
157
182
|
*/
|
|
158
|
-
setAge = async (
|
|
183
|
+
setAge = async (
|
|
184
|
+
age,
|
|
185
|
+
registrationID,
|
|
186
|
+
timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
187
|
+
inviteLinkURL = undefined,
|
|
188
|
+
) => {
|
|
159
189
|
if (age < 13) {
|
|
160
190
|
throw new SidechatAPIError("You're too young to use Offsides.");
|
|
161
191
|
}
|
|
@@ -166,6 +196,9 @@ class SidechatAPIClient {
|
|
|
166
196
|
body: JSON.stringify({
|
|
167
197
|
age: Number(age),
|
|
168
198
|
registration_id: registrationID,
|
|
199
|
+
time_zone: timeZone,
|
|
200
|
+
invite_link_url: inviteLinkURL,
|
|
201
|
+
device_id: this.defaultHeaders["X-Device-ID"],
|
|
169
202
|
}),
|
|
170
203
|
});
|
|
171
204
|
const json = await res.json();
|
|
@@ -248,8 +281,9 @@ class SidechatAPIClient {
|
|
|
248
281
|
* @method
|
|
249
282
|
* @since 1.0.0
|
|
250
283
|
* @param {String} deviceId - the device ID to set
|
|
284
|
+
* @param {"com.flowerave.sidechat"|"com.flowerave.yikyak"} [bundleID] - bundle ID of the app the push token belongs to
|
|
251
285
|
*/
|
|
252
|
-
setDeviceID = async (deviceID) => {
|
|
286
|
+
setDeviceID = async (deviceID, bundleID = "com.flowerave.sidechat") => {
|
|
253
287
|
if (!this.userToken) {
|
|
254
288
|
throw new SidechatAPIError("User is not authenticated.");
|
|
255
289
|
}
|
|
@@ -262,7 +296,7 @@ class SidechatAPIClient {
|
|
|
262
296
|
},
|
|
263
297
|
body: JSON.stringify({
|
|
264
298
|
build_type: "release",
|
|
265
|
-
bundle_id:
|
|
299
|
+
bundle_id: bundleID,
|
|
266
300
|
device_token: deviceID,
|
|
267
301
|
}),
|
|
268
302
|
});
|
|
@@ -322,9 +356,10 @@ class SidechatAPIClient {
|
|
|
322
356
|
* @param {String} groupID - group ID
|
|
323
357
|
* @param {"hot"|"recent"|"top"} category - category to filter posts
|
|
324
358
|
* @param {types.SidechatCursorString} [cursor] - cursor string
|
|
359
|
+
* @param {"day"|"week"|"month"|"year"|"all_time"} [period] - time period to pull top posts from (only if category is "top")
|
|
325
360
|
* @returns {Promise<types.SidechatPostsAndCursor>} List of posts and cursor
|
|
326
361
|
*/
|
|
327
|
-
getGroupPosts = async (groupID, category = "hot", cursor) => {
|
|
362
|
+
getGroupPosts = async (groupID, category = "hot", cursor, period) => {
|
|
328
363
|
if (!this.userToken) {
|
|
329
364
|
throw new SidechatAPIError("User is not authenticated.");
|
|
330
365
|
}
|
|
@@ -332,6 +367,8 @@ class SidechatAPIClient {
|
|
|
332
367
|
const res = await fetch(
|
|
333
368
|
`${this.apiRoot}/v1/posts?${
|
|
334
369
|
cursor ? "cursor=" + encodeURIComponent(cursor) + "&" : ""
|
|
370
|
+
}${
|
|
371
|
+
period ? "period=" + period + "&" : ""
|
|
335
372
|
}group_id=${groupID}&type=${category}&cacheBust=${Date.now()}`,
|
|
336
373
|
{
|
|
337
374
|
method: "GET",
|
|
@@ -428,7 +465,7 @@ class SidechatAPIClient {
|
|
|
428
465
|
contentType = "my_comments";
|
|
429
466
|
}
|
|
430
467
|
try {
|
|
431
|
-
const res = await fetch(`${this.apiRoot}/v1/posts
|
|
468
|
+
const res = await fetch(`${this.apiRoot}/v1/posts?type=${contentType}`, {
|
|
432
469
|
method: "GET",
|
|
433
470
|
headers: {
|
|
434
471
|
...this.defaultHeaders,
|
|
@@ -668,8 +705,9 @@ class SidechatAPIClient {
|
|
|
668
705
|
throw new SidechatAPIError("User is not authenticated.");
|
|
669
706
|
}
|
|
670
707
|
try {
|
|
708
|
+
// Joining moved to the v2 endpoint, which takes a list of IDs. Leave still sends group_id by itself, no list.
|
|
671
709
|
const res = await fetch(
|
|
672
|
-
`${this.apiRoot}
|
|
710
|
+
`${this.apiRoot}${isMember ? "/v2/groups/join" : "/v1/groups/leave"}`,
|
|
673
711
|
{
|
|
674
712
|
method: "POST",
|
|
675
713
|
headers: {
|
|
@@ -677,7 +715,8 @@ class SidechatAPIClient {
|
|
|
677
715
|
Authorization: `Bearer ${this.userToken}`,
|
|
678
716
|
},
|
|
679
717
|
body: JSON.stringify({
|
|
680
|
-
group_id: groupID,
|
|
718
|
+
group_id: isMember ? undefined : groupID,
|
|
719
|
+
group_ids: [groupID],
|
|
681
720
|
}),
|
|
682
721
|
},
|
|
683
722
|
);
|
|
@@ -736,15 +775,17 @@ class SidechatAPIClient {
|
|
|
736
775
|
}),
|
|
737
776
|
});
|
|
738
777
|
const json = await res.json();
|
|
778
|
+
if (!res.ok) throw new SidechatAPIError(json.message);
|
|
739
779
|
return await json.comment;
|
|
740
780
|
} catch (err) {
|
|
741
781
|
console.error(err);
|
|
782
|
+
if (err instanceof SidechatAPIError && err.message) throw err;
|
|
742
783
|
throw new SidechatAPIError(`Failed to post comment.`);
|
|
743
784
|
}
|
|
744
785
|
};
|
|
745
786
|
|
|
746
787
|
/**
|
|
747
|
-
* Creates a new post in the specified group
|
|
788
|
+
* Creates a new post in the specified group. Users can only make two posts a minute; if a post is rejected, the thrown error contains the reason from the server.
|
|
748
789
|
* @method
|
|
749
790
|
* @since 2.2.0
|
|
750
791
|
* @param {String} text - text content of comment
|
|
@@ -754,6 +795,7 @@ class SidechatAPIClient {
|
|
|
754
795
|
* @param {Boolean} [disableComments] - whether or not comments should be disabled on post
|
|
755
796
|
* @param {Boolean} [anonymous] - whether or not to hide user's name and icon on post
|
|
756
797
|
* @param {Array<String>} [pollOptions] - List of poll options. If provided, a poll will be created with these options.
|
|
798
|
+
* @param {Array<String>} [attachmentIDs] - IDs of link attachments created with createLinkAttachment()
|
|
757
799
|
* @returns {Promise<types.SidechatPostOrComment>} the created post
|
|
758
800
|
*/
|
|
759
801
|
createPost = async (
|
|
@@ -765,6 +807,7 @@ class SidechatAPIClient {
|
|
|
765
807
|
anonymous = false,
|
|
766
808
|
repostId = undefined,
|
|
767
809
|
pollOptions = undefined,
|
|
810
|
+
attachmentIDs = [],
|
|
768
811
|
) => {
|
|
769
812
|
if (!this.userToken) {
|
|
770
813
|
throw new SidechatAPIError("User is not authenticated.");
|
|
@@ -776,7 +819,7 @@ class SidechatAPIClient {
|
|
|
776
819
|
assets: assetList,
|
|
777
820
|
group_ids: [groupID],
|
|
778
821
|
text: text,
|
|
779
|
-
attachments:
|
|
822
|
+
attachments: attachmentIDs,
|
|
780
823
|
dms_disabled: disableDMs,
|
|
781
824
|
comments_disabled: disableComments,
|
|
782
825
|
using_identity: !anonymous,
|
|
@@ -798,9 +841,11 @@ class SidechatAPIClient {
|
|
|
798
841
|
body: JSON.stringify(body),
|
|
799
842
|
});
|
|
800
843
|
const json = await res.json();
|
|
844
|
+
if (!res.ok) throw new SidechatAPIError(json.message);
|
|
801
845
|
return await json.posts[0];
|
|
802
846
|
} catch (err) {
|
|
803
847
|
console.error(err);
|
|
848
|
+
if (err instanceof SidechatAPIError && err.message) throw err;
|
|
804
849
|
throw new SidechatAPIError(`Failed to make post.`);
|
|
805
850
|
}
|
|
806
851
|
};
|
|
@@ -877,7 +922,7 @@ class SidechatAPIClient {
|
|
|
877
922
|
}
|
|
878
923
|
try {
|
|
879
924
|
const res = await fetch(
|
|
880
|
-
`${this.apiRoot}/v1/polls/view_results
|
|
925
|
+
`${this.apiRoot}/v1/polls/view_results?cacheBust=${Date.now()}`,
|
|
881
926
|
{
|
|
882
927
|
method: "POST",
|
|
883
928
|
headers: {
|
|
@@ -898,22 +943,27 @@ class SidechatAPIClient {
|
|
|
898
943
|
};
|
|
899
944
|
|
|
900
945
|
/**
|
|
901
|
-
* Uploads an asset to AWS S3 for use in posts and comments.
|
|
946
|
+
* Uploads an asset to AWS S3 for use in posts and comments. Supports images and videos (videos must be under 50MB and are processed after upload)
|
|
902
947
|
* @method
|
|
903
948
|
* @param {String} uri - URI of the asset to upload
|
|
904
|
-
* @param {String} mimeType - mimetype of the asset (e.g. "image/png")
|
|
949
|
+
* @param {String} mimeType - mimetype of the asset (e.g. "image/png" or "video/mp4")
|
|
905
950
|
* @param {String} [name] - filename of the asset
|
|
951
|
+
* @param {"post"|"post_vod_source"|"chat"|"chat_icon"|"group_icon"|"profile_picture_icon"} [context] - where the asset will be used. Videos default to "post_vod_source", which is required for them to be processed into a playable stream.
|
|
906
952
|
* @returns {Promise<String>} URL of the uploaded asset
|
|
907
953
|
* @since 2.5.1
|
|
908
954
|
*/
|
|
909
|
-
uploadAsset = async (uri, mimeType, name = "") => {
|
|
955
|
+
uploadAsset = async (uri, mimeType, name = "", context = undefined) => {
|
|
910
956
|
if (!this.userToken) {
|
|
911
957
|
throw new SidechatAPIError("User is not authenticated.");
|
|
912
958
|
}
|
|
913
959
|
|
|
914
960
|
let imageType = mimeType.split("/")[1];
|
|
915
|
-
if (
|
|
916
|
-
|
|
961
|
+
if (imageType == "quicktime") imageType = "mov";
|
|
962
|
+
if (!["png", "jpeg", "gif", "webp", "mp4", "mov"].includes(imageType)) {
|
|
963
|
+
throw new SidechatAPIError("Unsupported file format.");
|
|
964
|
+
}
|
|
965
|
+
if (!context && ["mp4", "mov"].includes(imageType)) {
|
|
966
|
+
context = "post_vod_source";
|
|
917
967
|
}
|
|
918
968
|
|
|
919
969
|
const data = new FormData();
|
|
@@ -924,7 +974,9 @@ class SidechatAPIClient {
|
|
|
924
974
|
});
|
|
925
975
|
|
|
926
976
|
const urlReq = await this.sendRequest(
|
|
927
|
-
`/v1/assets/upload_url?content_type=${imageType}
|
|
977
|
+
`/v1/assets/upload_url?content_type=${imageType}${
|
|
978
|
+
context ? "&context=" + context : ""
|
|
979
|
+
}`,
|
|
928
980
|
);
|
|
929
981
|
const urlJson = await urlReq.json();
|
|
930
982
|
|
|
@@ -1180,7 +1232,7 @@ class SidechatAPIClient {
|
|
|
1180
1232
|
}
|
|
1181
1233
|
try {
|
|
1182
1234
|
const res = await fetch(
|
|
1183
|
-
`${this.apiRoot}/v1/chats/explore
|
|
1235
|
+
`${this.apiRoot}/v1/chats/explore?cacheBust=${Date.now()}`,
|
|
1184
1236
|
{
|
|
1185
1237
|
method: "GET",
|
|
1186
1238
|
headers: {
|
|
@@ -1245,21 +1297,27 @@ class SidechatAPIClient {
|
|
|
1245
1297
|
/**
|
|
1246
1298
|
* Gets a list of the user's direct messages
|
|
1247
1299
|
* @method
|
|
1300
|
+
* @param {types.SidechatCursorString} [cursor] - cursor string
|
|
1248
1301
|
* @returns {Promise<types.SidechatDirectThread[]>}
|
|
1249
1302
|
* @since 2.4.4
|
|
1250
1303
|
*/
|
|
1251
|
-
getDMs = async () => {
|
|
1304
|
+
getDMs = async (cursor) => {
|
|
1252
1305
|
if (!this.userToken) {
|
|
1253
1306
|
throw new SidechatAPIError("User is not authenticated.");
|
|
1254
1307
|
}
|
|
1255
1308
|
try {
|
|
1256
|
-
const res = await fetch(
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1309
|
+
const res = await fetch(
|
|
1310
|
+
`${this.apiRoot}/v1/chats${
|
|
1311
|
+
cursor ? "?cursor=" + encodeURIComponent(cursor) : ""
|
|
1312
|
+
}`,
|
|
1313
|
+
{
|
|
1314
|
+
method: "GET",
|
|
1315
|
+
headers: {
|
|
1316
|
+
...this.defaultHeaders,
|
|
1317
|
+
Authorization: `Bearer ${this.userToken}`,
|
|
1318
|
+
},
|
|
1261
1319
|
},
|
|
1262
|
-
|
|
1320
|
+
);
|
|
1263
1321
|
const json = await res.json();
|
|
1264
1322
|
const list = await json.chats;
|
|
1265
1323
|
let r = [];
|
|
@@ -1388,6 +1446,7 @@ class SidechatAPIClient {
|
|
|
1388
1446
|
* @method
|
|
1389
1447
|
* @param {String} postID - alphanumeric ID of post to hide
|
|
1390
1448
|
* @since 2.6.2
|
|
1449
|
+
* @deprecated since 2.7.0. Please use `blockUser` instead!
|
|
1391
1450
|
*/
|
|
1392
1451
|
hidePostsFromUser = async (postID) => {
|
|
1393
1452
|
if (!this.userToken) {
|
|
@@ -1418,6 +1477,7 @@ class SidechatAPIClient {
|
|
|
1418
1477
|
* Unhides all posts from all users
|
|
1419
1478
|
* @method
|
|
1420
1479
|
* @since 2.6.2
|
|
1480
|
+
* @deprecated since 2.7.0. No longer used by the official app and may stop working.
|
|
1421
1481
|
*/
|
|
1422
1482
|
unhidePostsFromAllUsers = async () => {
|
|
1423
1483
|
if (!this.userToken) {
|
|
@@ -1439,6 +1499,396 @@ class SidechatAPIClient {
|
|
|
1439
1499
|
throw new SidechatAPIError(`Failed to unhide post.`);
|
|
1440
1500
|
}
|
|
1441
1501
|
};
|
|
1502
|
+
|
|
1503
|
+
/**
|
|
1504
|
+
* Fetches the home feed, which mixes posts from all of the user's groups
|
|
1505
|
+
* @method
|
|
1506
|
+
* @param {"for_you"|"hot"|"recent"} [type] - how the feed should be sorted
|
|
1507
|
+
* @param {String} [schoolGroupID] - alphanumeric ID of the user's school group
|
|
1508
|
+
* @param {types.SidechatCursorString} [cursor] - cursor string
|
|
1509
|
+
* @returns {Promise<types.SidechatFeed>} feed items and cursor
|
|
1510
|
+
* @since 2.7.0
|
|
1511
|
+
*/
|
|
1512
|
+
getHomeFeed = async (type = "for_you", schoolGroupID, cursor) => {
|
|
1513
|
+
if (!this.userToken) {
|
|
1514
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1515
|
+
}
|
|
1516
|
+
try {
|
|
1517
|
+
const params = new URLSearchParams({ type: type });
|
|
1518
|
+
if (schoolGroupID) params.set("school_group_id", schoolGroupID);
|
|
1519
|
+
if (cursor) params.set("cursor", cursor);
|
|
1520
|
+
const res = await this.sendRequest(`/v1/posts/home?${params}`);
|
|
1521
|
+
return await res.json();
|
|
1522
|
+
} catch (err) {
|
|
1523
|
+
console.error(err);
|
|
1524
|
+
throw new SidechatAPIError(`Failed to get home feed.`);
|
|
1525
|
+
}
|
|
1526
|
+
};
|
|
1527
|
+
|
|
1528
|
+
/**
|
|
1529
|
+
* Follows or unfollows a post. Followed posts send activity notifications when they get new comments.
|
|
1530
|
+
* @method
|
|
1531
|
+
* @param {String} postID - alphanumeric ID of post
|
|
1532
|
+
* @param {Boolean} following - whether or not the user should follow the post
|
|
1533
|
+
* @returns {Promise<{post: types.SidechatPostOrComment}>} the updated post
|
|
1534
|
+
* @since 2.7.0
|
|
1535
|
+
*/
|
|
1536
|
+
setPostFollowing = async (postID, following) => {
|
|
1537
|
+
if (!this.userToken) {
|
|
1538
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1539
|
+
}
|
|
1540
|
+
try {
|
|
1541
|
+
const res = await this.sendRequest(
|
|
1542
|
+
`/v1/posts/${following ? "follow" : "unfollow"}`,
|
|
1543
|
+
"POST",
|
|
1544
|
+
JSON.stringify({ post_id: postID }),
|
|
1545
|
+
);
|
|
1546
|
+
return await res.json();
|
|
1547
|
+
} catch (err) {
|
|
1548
|
+
console.error(err);
|
|
1549
|
+
throw new SidechatAPIError(`Failed to change post follow status.`);
|
|
1550
|
+
}
|
|
1551
|
+
};
|
|
1552
|
+
|
|
1553
|
+
/**
|
|
1554
|
+
* Reports a post or comment to the group's moderators
|
|
1555
|
+
* @method
|
|
1556
|
+
* @param {String} postID - alphanumeric ID of post or comment to report
|
|
1557
|
+
* @param {types.SidechatReportReason} reason - why the content is being reported
|
|
1558
|
+
* @param {String} [note] - explanation to send along with the report
|
|
1559
|
+
* @since 2.7.0
|
|
1560
|
+
*/
|
|
1561
|
+
flagPost = async (postID, reason, note = undefined) => {
|
|
1562
|
+
if (!this.userToken) {
|
|
1563
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1564
|
+
}
|
|
1565
|
+
try {
|
|
1566
|
+
const res = await this.sendRequest(
|
|
1567
|
+
"/v1/posts/flag",
|
|
1568
|
+
"POST",
|
|
1569
|
+
JSON.stringify({ post_id: postID, reason: reason, note: note }),
|
|
1570
|
+
);
|
|
1571
|
+
return await res.json();
|
|
1572
|
+
} catch (err) {
|
|
1573
|
+
console.error(err);
|
|
1574
|
+
throw new SidechatAPIError(`Failed to report post.`);
|
|
1575
|
+
}
|
|
1576
|
+
};
|
|
1577
|
+
|
|
1578
|
+
/**
|
|
1579
|
+
* Blocks the author of a post or comment, hiding all of their content from the user. Replaces `hidePostsFromUser`.
|
|
1580
|
+
* @method
|
|
1581
|
+
* @param {String} postID - alphanumeric ID of a post or comment by the user to block
|
|
1582
|
+
* @param {Boolean} [report] - whether or not to also report the content
|
|
1583
|
+
* @param {"feed"|"chat"} [postContext] - where the content was seen
|
|
1584
|
+
* @since 2.7.0
|
|
1585
|
+
*/
|
|
1586
|
+
blockUser = async (postID, report = false, postContext = "feed") => {
|
|
1587
|
+
if (!this.userToken) {
|
|
1588
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1589
|
+
}
|
|
1590
|
+
try {
|
|
1591
|
+
const res = await this.sendRequest(
|
|
1592
|
+
"/v1/posts/block_user",
|
|
1593
|
+
"POST",
|
|
1594
|
+
JSON.stringify({
|
|
1595
|
+
post_id: postID,
|
|
1596
|
+
post_context: postContext,
|
|
1597
|
+
report: report,
|
|
1598
|
+
}),
|
|
1599
|
+
);
|
|
1600
|
+
return await res.json();
|
|
1601
|
+
} catch (err) {
|
|
1602
|
+
console.error(err);
|
|
1603
|
+
throw new SidechatAPIError(`Failed to block user.`);
|
|
1604
|
+
}
|
|
1605
|
+
};
|
|
1606
|
+
|
|
1607
|
+
/**
|
|
1608
|
+
* Creates a link attachment that can be added to a post with createPost(). YouTube links are recognized and shown as embedded videos.
|
|
1609
|
+
* @method
|
|
1610
|
+
* @param {String} url - URL to attach
|
|
1611
|
+
* @returns {Promise<types.SidechatAttachment>} the created attachment
|
|
1612
|
+
* @since 2.7.0
|
|
1613
|
+
*/
|
|
1614
|
+
createLinkAttachment = async (url) => {
|
|
1615
|
+
if (!this.userToken) {
|
|
1616
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1617
|
+
}
|
|
1618
|
+
try {
|
|
1619
|
+
const res = await this.sendRequest(
|
|
1620
|
+
"/v1/attachments/upload_link",
|
|
1621
|
+
"POST",
|
|
1622
|
+
JSON.stringify({ link_url: url }),
|
|
1623
|
+
);
|
|
1624
|
+
return await res.json();
|
|
1625
|
+
} catch (err) {
|
|
1626
|
+
console.error(err);
|
|
1627
|
+
throw new SidechatAPIError(`Failed to create link attachment.`);
|
|
1628
|
+
}
|
|
1629
|
+
};
|
|
1630
|
+
|
|
1631
|
+
/**
|
|
1632
|
+
* Gets the user's activity feed (notifications for comments, votes, followers, etc.)
|
|
1633
|
+
* @method
|
|
1634
|
+
* @param {types.SidechatCursorString} [cursor] - cursor string
|
|
1635
|
+
* @returns {Promise<types.SidechatActivityList>} activity items and cursor
|
|
1636
|
+
* @since 2.7.0
|
|
1637
|
+
*/
|
|
1638
|
+
getActivity = async (cursor) => {
|
|
1639
|
+
if (!this.userToken) {
|
|
1640
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1641
|
+
}
|
|
1642
|
+
try {
|
|
1643
|
+
const res = await this.sendRequest(
|
|
1644
|
+
`/v1/activity${cursor ? "?cursor=" + encodeURIComponent(cursor) : ""}`,
|
|
1645
|
+
);
|
|
1646
|
+
return await res.json();
|
|
1647
|
+
} catch (err) {
|
|
1648
|
+
console.error(err);
|
|
1649
|
+
throw new SidechatAPIError(`Failed to get activity.`);
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
|
|
1653
|
+
/**
|
|
1654
|
+
* Gets the groups the user is a member of
|
|
1655
|
+
* @method
|
|
1656
|
+
* @returns {Promise<types.SidechatGroup[]>}
|
|
1657
|
+
* @since 2.7.0
|
|
1658
|
+
*/
|
|
1659
|
+
getGroups = async () => {
|
|
1660
|
+
if (!this.userToken) {
|
|
1661
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1662
|
+
}
|
|
1663
|
+
try {
|
|
1664
|
+
const res = await this.sendRequest("/v1/groups");
|
|
1665
|
+
const json = await res.json();
|
|
1666
|
+
return json.groups;
|
|
1667
|
+
} catch (err) {
|
|
1668
|
+
console.error(err);
|
|
1669
|
+
throw new SidechatAPIError(`Failed to get groups.`);
|
|
1670
|
+
}
|
|
1671
|
+
};
|
|
1672
|
+
|
|
1673
|
+
/**
|
|
1674
|
+
* Gets groups suggested for the user to join
|
|
1675
|
+
* @method
|
|
1676
|
+
* @param {Number} [limit] - maximum number of groups to return
|
|
1677
|
+
* @returns {Promise<types.SidechatGroup[]>}
|
|
1678
|
+
* @since 2.7.0
|
|
1679
|
+
*/
|
|
1680
|
+
getSuggestedGroups = async (limit = 10) => {
|
|
1681
|
+
if (!this.userToken) {
|
|
1682
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1683
|
+
}
|
|
1684
|
+
try {
|
|
1685
|
+
const res = await this.sendRequest(
|
|
1686
|
+
`/v1/groups/suggestions?limit=${limit}`,
|
|
1687
|
+
);
|
|
1688
|
+
const json = await res.json();
|
|
1689
|
+
return json.groups;
|
|
1690
|
+
} catch (err) {
|
|
1691
|
+
console.error(err);
|
|
1692
|
+
throw new SidechatAPIError(`Failed to get suggested groups.`);
|
|
1693
|
+
}
|
|
1694
|
+
};
|
|
1695
|
+
|
|
1696
|
+
/**
|
|
1697
|
+
* Gets a shareable invite link for a group. School groups don't have invite links and will throw an error.
|
|
1698
|
+
* @method
|
|
1699
|
+
* @param {String} groupID - alphanumeric ID of group
|
|
1700
|
+
* @returns {Promise<String>} invite link URL
|
|
1701
|
+
* @since 2.7.0
|
|
1702
|
+
*/
|
|
1703
|
+
getGroupInviteLink = async (groupID) => {
|
|
1704
|
+
if (!this.userToken) {
|
|
1705
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1706
|
+
}
|
|
1707
|
+
try {
|
|
1708
|
+
const res = await this.sendRequest(
|
|
1709
|
+
`/v1/groups/invite_link?group_id=${groupID}`,
|
|
1710
|
+
);
|
|
1711
|
+
const json = await res.json();
|
|
1712
|
+
return json.link;
|
|
1713
|
+
} catch (err) {
|
|
1714
|
+
console.error(err);
|
|
1715
|
+
throw new SidechatAPIError(`Failed to get invite link.`);
|
|
1716
|
+
}
|
|
1717
|
+
};
|
|
1718
|
+
|
|
1719
|
+
/**
|
|
1720
|
+
* Gets the yakarma leaderboard for a group, along with the user's own karma
|
|
1721
|
+
* @method
|
|
1722
|
+
* @param {String} groupID - alphanumeric ID of group
|
|
1723
|
+
* @returns {Promise<types.SidechatLeaderboard>}
|
|
1724
|
+
* @since 2.7.0
|
|
1725
|
+
*/
|
|
1726
|
+
getLeaderboard = async (groupID) => {
|
|
1727
|
+
if (!this.userToken) {
|
|
1728
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1729
|
+
}
|
|
1730
|
+
try {
|
|
1731
|
+
const res = await this.sendRequest(`/v1/leaderboard?group_id=${groupID}`);
|
|
1732
|
+
return await res.json();
|
|
1733
|
+
} catch (err) {
|
|
1734
|
+
console.error(err);
|
|
1735
|
+
throw new SidechatAPIError(`Failed to get leaderboard.`);
|
|
1736
|
+
}
|
|
1737
|
+
};
|
|
1738
|
+
|
|
1739
|
+
/**
|
|
1740
|
+
* Gets the existing direct message thread for a post or comment. Returns undefined if the user hasn't started one.
|
|
1741
|
+
* @method
|
|
1742
|
+
* @param {String} postID - alphanumeric ID of post or comment
|
|
1743
|
+
* @returns {Promise<types.SidechatDirectThread>}
|
|
1744
|
+
* @since 2.7.0
|
|
1745
|
+
*/
|
|
1746
|
+
getDMForPost = async (postID) => {
|
|
1747
|
+
if (!this.userToken) {
|
|
1748
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1749
|
+
}
|
|
1750
|
+
try {
|
|
1751
|
+
const res = await this.sendRequest(`/v1/chats/by_post?post_id=${postID}`);
|
|
1752
|
+
const json = await res.json();
|
|
1753
|
+
return json.chat;
|
|
1754
|
+
} catch (err) {
|
|
1755
|
+
console.error(err);
|
|
1756
|
+
throw new SidechatAPIError(`Failed to get DM thread for post.`);
|
|
1757
|
+
}
|
|
1758
|
+
};
|
|
1759
|
+
|
|
1760
|
+
/**
|
|
1761
|
+
* Reports a direct message thread
|
|
1762
|
+
* @method
|
|
1763
|
+
* @param {String} chatID - alphanumeric ID of the thread to report
|
|
1764
|
+
* @param {types.SidechatReportReason} reason - why the thread is being reported
|
|
1765
|
+
* @since 2.7.0
|
|
1766
|
+
*/
|
|
1767
|
+
reportDM = async (chatID, reason) => {
|
|
1768
|
+
if (!this.userToken) {
|
|
1769
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1770
|
+
}
|
|
1771
|
+
try {
|
|
1772
|
+
const res = await this.sendRequest(
|
|
1773
|
+
"/v1/chats/report",
|
|
1774
|
+
"POST",
|
|
1775
|
+
JSON.stringify({ chat_id: chatID, reason: reason }),
|
|
1776
|
+
);
|
|
1777
|
+
return await res.json();
|
|
1778
|
+
} catch (err) {
|
|
1779
|
+
console.error(err);
|
|
1780
|
+
throw new SidechatAPIError(`Failed to report DM.`);
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1783
|
+
|
|
1784
|
+
/**
|
|
1785
|
+
* Sets which push notifications the user receives
|
|
1786
|
+
* @method
|
|
1787
|
+
* @param {types.SidechatNotificationPreferences} preferences - notification types to enable or disable
|
|
1788
|
+
* @since 2.7.0
|
|
1789
|
+
*/
|
|
1790
|
+
setNotificationPreferences = async (preferences) => {
|
|
1791
|
+
if (!this.userToken) {
|
|
1792
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1793
|
+
}
|
|
1794
|
+
try {
|
|
1795
|
+
const res = await this.sendRequest(
|
|
1796
|
+
"/v1/users/set_notification_preferences",
|
|
1797
|
+
"POST",
|
|
1798
|
+
JSON.stringify({ notification_preferences: preferences }),
|
|
1799
|
+
);
|
|
1800
|
+
return await res.json();
|
|
1801
|
+
} catch (err) {
|
|
1802
|
+
console.error(err);
|
|
1803
|
+
throw new SidechatAPIError(`Failed to set notification preferences.`);
|
|
1804
|
+
}
|
|
1805
|
+
};
|
|
1806
|
+
|
|
1807
|
+
/**
|
|
1808
|
+
* Verifies the user's school email by signing in with Google or Microsoft instead of clicking an emailed link. Returns the same object as checkEmailVerification(). This was discovered from me decompiling the new Android app, I haven't tested this but based on the implementation it should work fine and might resolve the problem that a lot of people have with their accounts not being associated with their school automatically.
|
|
1809
|
+
* @method
|
|
1810
|
+
* @param {String} authToken - token from the Google or Microsoft sign-in
|
|
1811
|
+
* @param {"google"|"microsoft"} authType - provider the token came from
|
|
1812
|
+
* @param {Boolean} [isIncomingFreshman] - whether or not the user is an incoming freshman
|
|
1813
|
+
* @since 2.7.0
|
|
1814
|
+
* @tutorial Email Registration
|
|
1815
|
+
*/
|
|
1816
|
+
verifyEmailWithThirdParty = async (
|
|
1817
|
+
authToken,
|
|
1818
|
+
authType,
|
|
1819
|
+
isIncomingFreshman = false,
|
|
1820
|
+
) => {
|
|
1821
|
+
if (!this.userToken) {
|
|
1822
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1823
|
+
}
|
|
1824
|
+
try {
|
|
1825
|
+
const res = await this.sendRequest(
|
|
1826
|
+
"/v1/users/third_party_email_verified",
|
|
1827
|
+
"POST",
|
|
1828
|
+
JSON.stringify({
|
|
1829
|
+
auth: authToken,
|
|
1830
|
+
auth_type: authType,
|
|
1831
|
+
is_incoming_freshman: isIncomingFreshman,
|
|
1832
|
+
}),
|
|
1833
|
+
);
|
|
1834
|
+
const json = await res.json();
|
|
1835
|
+
if (json.verified_email_updates_response) {
|
|
1836
|
+
return json.verified_email_updates_response;
|
|
1837
|
+
} else {
|
|
1838
|
+
throw new SidechatAPIError(json?.message || "Email is not verified.");
|
|
1839
|
+
}
|
|
1840
|
+
} catch (err) {
|
|
1841
|
+
console.error(err);
|
|
1842
|
+
throw new SidechatAPIError("Failed to verify email.");
|
|
1843
|
+
}
|
|
1844
|
+
};
|
|
1845
|
+
|
|
1846
|
+
/**
|
|
1847
|
+
* Authorizes a realtime (Pusher) connection to subscribe to a private channel, such as `private-chat-<chatID>` for live direct messages. Pass the result to your Pusher client's channel authorizer. This hasn't been tested, and is mostly theoretical.
|
|
1848
|
+
* @method
|
|
1849
|
+
* @param {String} socketID - socket ID assigned by Pusher
|
|
1850
|
+
* @param {String} channelName - name of the private channel to subscribe to
|
|
1851
|
+
* @returns {Promise<{auth: String}>} signed channel authorization
|
|
1852
|
+
* @since 2.7.0
|
|
1853
|
+
*/
|
|
1854
|
+
authorizeRealtimeChannel = async (socketID, channelName) => {
|
|
1855
|
+
if (!this.userToken) {
|
|
1856
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1857
|
+
}
|
|
1858
|
+
try {
|
|
1859
|
+
const res = await this.sendRequest(
|
|
1860
|
+
"/pusher/auth",
|
|
1861
|
+
"POST",
|
|
1862
|
+
JSON.stringify({ socket_id: socketID, channel_name: channelName }),
|
|
1863
|
+
);
|
|
1864
|
+
return await res.json();
|
|
1865
|
+
} catch (err) {
|
|
1866
|
+
console.error(err);
|
|
1867
|
+
throw new SidechatAPIError(`Failed to authorize realtime channel.`);
|
|
1868
|
+
}
|
|
1869
|
+
};
|
|
1870
|
+
|
|
1871
|
+
/**
|
|
1872
|
+
* Permanently deletes the current user's account. This cannot be undone!
|
|
1873
|
+
* @method
|
|
1874
|
+
* @since 2.7.0
|
|
1875
|
+
*/
|
|
1876
|
+
deleteAccount = async () => {
|
|
1877
|
+
if (!this.userToken) {
|
|
1878
|
+
throw new SidechatAPIError("User is not authenticated.");
|
|
1879
|
+
}
|
|
1880
|
+
try {
|
|
1881
|
+
const res = await this.sendRequest(
|
|
1882
|
+
"/v1/users/delete",
|
|
1883
|
+
"POST",
|
|
1884
|
+
JSON.stringify({}),
|
|
1885
|
+
);
|
|
1886
|
+
return await res.json();
|
|
1887
|
+
} catch (err) {
|
|
1888
|
+
console.error(err);
|
|
1889
|
+
throw new SidechatAPIError(`Failed to delete account.`);
|
|
1890
|
+
}
|
|
1891
|
+
};
|
|
1442
1892
|
}
|
|
1443
1893
|
|
|
1444
1894
|
export default SidechatAPIClient;
|