sidechat.js 2.6.4 → 2.6.6

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.
Files changed (248) hide show
  1. package/.github/workflows/static.yml +0 -0
  2. package/README.md +0 -0
  3. package/dist/classes/SidechatAPIClient.d.ts +50 -47
  4. package/dist/classes/SidechatAPIClient.d.ts.map +1 -1
  5. package/dist/classes/SidechatAPIError.d.ts +0 -0
  6. package/dist/classes/SidechatAPIError.d.ts.map +0 -0
  7. package/dist/classes/SidechatColorList.d.ts +0 -0
  8. package/dist/classes/SidechatColorList.d.ts.map +0 -0
  9. package/dist/index.cjs +1439 -1408
  10. package/dist/index.d.mts +0 -0
  11. package/dist/index.d.ts +0 -0
  12. package/dist/index.d.ts.map +0 -0
  13. package/dist/index.mjs +1439 -1408
  14. package/dist/types/SidechatTypes.d.ts +0 -0
  15. package/dist/types/SidechatTypes.d.ts.map +0 -0
  16. package/docs/sidechat.js/2.5.6/SidechatAPIClient.html +0 -0
  17. package/docs/sidechat.js/2.5.6/SidechatAPIError.html +0 -0
  18. package/docs/sidechat.js/2.5.6/SidechatColorList.html +0 -0
  19. package/docs/sidechat.js/2.5.6/classes_SidechatAPIClient.js.html +0 -0
  20. package/docs/sidechat.js/2.5.6/classes_SidechatAPIError.js.html +0 -0
  21. package/docs/sidechat.js/2.5.6/classes_SidechatColorList.js.html +0 -0
  22. package/docs/sidechat.js/2.5.6/data/search.json +0 -0
  23. package/docs/sidechat.js/2.5.6/fonts/Inconsolata-Regular.ttf +0 -0
  24. package/docs/sidechat.js/2.5.6/fonts/OpenSans-Regular.ttf +0 -0
  25. package/docs/sidechat.js/2.5.6/fonts/WorkSans-Bold.ttf +0 -0
  26. package/docs/sidechat.js/2.5.6/global.html +0 -0
  27. package/docs/sidechat.js/2.5.6/index.html +0 -0
  28. package/docs/sidechat.js/2.5.6/scripts/core.js +0 -0
  29. package/docs/sidechat.js/2.5.6/scripts/core.min.js +0 -0
  30. package/docs/sidechat.js/2.5.6/scripts/resize.js +0 -0
  31. package/docs/sidechat.js/2.5.6/scripts/search.js +0 -0
  32. package/docs/sidechat.js/2.5.6/scripts/search.min.js +0 -0
  33. package/docs/sidechat.js/2.5.6/scripts/third-party/Apache-License-2.0.txt +0 -0
  34. package/docs/sidechat.js/2.5.6/scripts/third-party/fuse.js +0 -0
  35. package/docs/sidechat.js/2.5.6/scripts/third-party/hljs-line-num-original.js +0 -0
  36. package/docs/sidechat.js/2.5.6/scripts/third-party/hljs-line-num.js +0 -0
  37. package/docs/sidechat.js/2.5.6/scripts/third-party/hljs-original.js +0 -0
  38. package/docs/sidechat.js/2.5.6/scripts/third-party/hljs.js +0 -0
  39. package/docs/sidechat.js/2.5.6/scripts/third-party/popper.js +0 -0
  40. package/docs/sidechat.js/2.5.6/scripts/third-party/tippy.js +0 -0
  41. package/docs/sidechat.js/2.5.6/scripts/third-party/tocbot.js +0 -0
  42. package/docs/sidechat.js/2.5.6/scripts/third-party/tocbot.min.js +0 -0
  43. package/docs/sidechat.js/2.5.6/styles/clean-jsdoc-theme-base.css +0 -0
  44. package/docs/sidechat.js/2.5.6/styles/clean-jsdoc-theme-dark.css +0 -0
  45. package/docs/sidechat.js/2.5.6/styles/clean-jsdoc-theme-light.css +0 -0
  46. package/docs/sidechat.js/2.5.6/styles/clean-jsdoc-theme-scrollbar.css +0 -0
  47. package/docs/sidechat.js/2.5.6/styles/clean-jsdoc-theme-without-scrollbar.min.css +0 -0
  48. package/docs/sidechat.js/2.5.6/styles/clean-jsdoc-theme.min.css +0 -0
  49. package/docs/sidechat.js/2.5.6/tutorial-Authentication.html +0 -0
  50. package/docs/sidechat.js/2.5.6/tutorial-Email Registration.html +0 -0
  51. package/docs/sidechat.js/2.5.6/types_SidechatTypes.js.html +0 -0
  52. package/docs/sidechat.js/2.6.0/SidechatAPIClient.html +0 -0
  53. package/docs/sidechat.js/2.6.0/SidechatAPIError.html +0 -0
  54. package/docs/sidechat.js/2.6.0/SidechatColorList.html +0 -0
  55. package/docs/sidechat.js/2.6.0/classes_SidechatAPIClient.js.html +0 -0
  56. package/docs/sidechat.js/2.6.0/classes_SidechatAPIError.js.html +0 -0
  57. package/docs/sidechat.js/2.6.0/classes_SidechatColorList.js.html +0 -0
  58. package/docs/sidechat.js/2.6.0/data/search.json +0 -0
  59. package/docs/sidechat.js/2.6.0/fonts/Inconsolata-Regular.ttf +0 -0
  60. package/docs/sidechat.js/2.6.0/fonts/OpenSans-Regular.ttf +0 -0
  61. package/docs/sidechat.js/2.6.0/fonts/WorkSans-Bold.ttf +0 -0
  62. package/docs/sidechat.js/2.6.0/global.html +0 -0
  63. package/docs/sidechat.js/2.6.0/index.html +0 -0
  64. package/docs/sidechat.js/2.6.0/scripts/core.js +0 -0
  65. package/docs/sidechat.js/2.6.0/scripts/core.min.js +0 -0
  66. package/docs/sidechat.js/2.6.0/scripts/resize.js +0 -0
  67. package/docs/sidechat.js/2.6.0/scripts/search.js +0 -0
  68. package/docs/sidechat.js/2.6.0/scripts/search.min.js +0 -0
  69. package/docs/sidechat.js/2.6.0/scripts/third-party/Apache-License-2.0.txt +0 -0
  70. package/docs/sidechat.js/2.6.0/scripts/third-party/fuse.js +0 -0
  71. package/docs/sidechat.js/2.6.0/scripts/third-party/hljs-line-num-original.js +0 -0
  72. package/docs/sidechat.js/2.6.0/scripts/third-party/hljs-line-num.js +0 -0
  73. package/docs/sidechat.js/2.6.0/scripts/third-party/hljs-original.js +0 -0
  74. package/docs/sidechat.js/2.6.0/scripts/third-party/hljs.js +0 -0
  75. package/docs/sidechat.js/2.6.0/scripts/third-party/popper.js +0 -0
  76. package/docs/sidechat.js/2.6.0/scripts/third-party/tippy.js +0 -0
  77. package/docs/sidechat.js/2.6.0/scripts/third-party/tocbot.js +0 -0
  78. package/docs/sidechat.js/2.6.0/scripts/third-party/tocbot.min.js +0 -0
  79. package/docs/sidechat.js/2.6.0/styles/clean-jsdoc-theme-base.css +0 -0
  80. package/docs/sidechat.js/2.6.0/styles/clean-jsdoc-theme-dark.css +0 -0
  81. package/docs/sidechat.js/2.6.0/styles/clean-jsdoc-theme-light.css +0 -0
  82. package/docs/sidechat.js/2.6.0/styles/clean-jsdoc-theme-scrollbar.css +0 -0
  83. package/docs/sidechat.js/2.6.0/styles/clean-jsdoc-theme-without-scrollbar.min.css +0 -0
  84. package/docs/sidechat.js/2.6.0/styles/clean-jsdoc-theme.min.css +0 -0
  85. package/docs/sidechat.js/2.6.0/tutorial-Authentication.html +0 -0
  86. package/docs/sidechat.js/2.6.0/tutorial-Email Registration.html +0 -0
  87. package/docs/sidechat.js/2.6.0/types_SidechatTypes.js.html +0 -0
  88. package/docs/sidechat.js/2.6.1/SidechatAPIClient.html +0 -0
  89. package/docs/sidechat.js/2.6.1/SidechatAPIError.html +0 -0
  90. package/docs/sidechat.js/2.6.1/SidechatColorList.html +0 -0
  91. package/docs/sidechat.js/2.6.1/classes_SidechatAPIClient.js.html +0 -0
  92. package/docs/sidechat.js/2.6.1/classes_SidechatAPIError.js.html +0 -0
  93. package/docs/sidechat.js/2.6.1/classes_SidechatColorList.js.html +0 -0
  94. package/docs/sidechat.js/2.6.1/data/search.json +0 -0
  95. package/docs/sidechat.js/2.6.1/fonts/Inconsolata-Regular.ttf +0 -0
  96. package/docs/sidechat.js/2.6.1/fonts/OpenSans-Regular.ttf +0 -0
  97. package/docs/sidechat.js/2.6.1/fonts/WorkSans-Bold.ttf +0 -0
  98. package/docs/sidechat.js/2.6.1/global.html +0 -0
  99. package/docs/sidechat.js/2.6.1/index.html +0 -0
  100. package/docs/sidechat.js/2.6.1/scripts/core.js +0 -0
  101. package/docs/sidechat.js/2.6.1/scripts/core.min.js +0 -0
  102. package/docs/sidechat.js/2.6.1/scripts/resize.js +0 -0
  103. package/docs/sidechat.js/2.6.1/scripts/search.js +0 -0
  104. package/docs/sidechat.js/2.6.1/scripts/search.min.js +0 -0
  105. package/docs/sidechat.js/2.6.1/scripts/third-party/Apache-License-2.0.txt +0 -0
  106. package/docs/sidechat.js/2.6.1/scripts/third-party/fuse.js +0 -0
  107. package/docs/sidechat.js/2.6.1/scripts/third-party/hljs-line-num-original.js +0 -0
  108. package/docs/sidechat.js/2.6.1/scripts/third-party/hljs-line-num.js +0 -0
  109. package/docs/sidechat.js/2.6.1/scripts/third-party/hljs-original.js +0 -0
  110. package/docs/sidechat.js/2.6.1/scripts/third-party/hljs.js +0 -0
  111. package/docs/sidechat.js/2.6.1/scripts/third-party/popper.js +0 -0
  112. package/docs/sidechat.js/2.6.1/scripts/third-party/tippy.js +0 -0
  113. package/docs/sidechat.js/2.6.1/scripts/third-party/tocbot.js +0 -0
  114. package/docs/sidechat.js/2.6.1/scripts/third-party/tocbot.min.js +0 -0
  115. package/docs/sidechat.js/2.6.1/styles/clean-jsdoc-theme-base.css +0 -0
  116. package/docs/sidechat.js/2.6.1/styles/clean-jsdoc-theme-dark.css +0 -0
  117. package/docs/sidechat.js/2.6.1/styles/clean-jsdoc-theme-light.css +0 -0
  118. package/docs/sidechat.js/2.6.1/styles/clean-jsdoc-theme-scrollbar.css +0 -0
  119. package/docs/sidechat.js/2.6.1/styles/clean-jsdoc-theme-without-scrollbar.min.css +0 -0
  120. package/docs/sidechat.js/2.6.1/styles/clean-jsdoc-theme.min.css +0 -0
  121. package/docs/sidechat.js/2.6.1/tutorial-Authentication.html +0 -0
  122. package/docs/sidechat.js/2.6.1/tutorial-Email Registration.html +0 -0
  123. package/docs/sidechat.js/2.6.1/types_SidechatTypes.js.html +0 -0
  124. package/docs/sidechat.js/2.6.4/SidechatAPIClient.html +0 -0
  125. package/docs/sidechat.js/2.6.4/SidechatAPIError.html +0 -0
  126. package/docs/sidechat.js/2.6.4/SidechatColorList.html +0 -0
  127. package/docs/sidechat.js/2.6.4/classes_SidechatAPIClient.js.html +0 -0
  128. package/docs/sidechat.js/2.6.4/classes_SidechatAPIError.js.html +0 -0
  129. package/docs/sidechat.js/2.6.4/classes_SidechatColorList.js.html +0 -0
  130. package/docs/sidechat.js/2.6.4/data/search.json +0 -0
  131. package/docs/sidechat.js/2.6.4/fonts/Inconsolata-Regular.ttf +0 -0
  132. package/docs/sidechat.js/2.6.4/fonts/OpenSans-Regular.ttf +0 -0
  133. package/docs/sidechat.js/2.6.4/fonts/WorkSans-Bold.ttf +0 -0
  134. package/docs/sidechat.js/2.6.4/global.html +0 -0
  135. package/docs/sidechat.js/2.6.4/index.html +0 -0
  136. package/docs/sidechat.js/2.6.4/scripts/core.js +0 -0
  137. package/docs/sidechat.js/2.6.4/scripts/core.min.js +0 -0
  138. package/docs/sidechat.js/2.6.4/scripts/resize.js +0 -0
  139. package/docs/sidechat.js/2.6.4/scripts/search.js +0 -0
  140. package/docs/sidechat.js/2.6.4/scripts/search.min.js +0 -0
  141. package/docs/sidechat.js/2.6.4/scripts/third-party/Apache-License-2.0.txt +0 -0
  142. package/docs/sidechat.js/2.6.4/scripts/third-party/fuse.js +0 -0
  143. package/docs/sidechat.js/2.6.4/scripts/third-party/hljs-line-num-original.js +0 -0
  144. package/docs/sidechat.js/2.6.4/scripts/third-party/hljs-line-num.js +0 -0
  145. package/docs/sidechat.js/2.6.4/scripts/third-party/hljs-original.js +0 -0
  146. package/docs/sidechat.js/2.6.4/scripts/third-party/hljs.js +0 -0
  147. package/docs/sidechat.js/2.6.4/scripts/third-party/popper.js +0 -0
  148. package/docs/sidechat.js/2.6.4/scripts/third-party/tippy.js +0 -0
  149. package/docs/sidechat.js/2.6.4/scripts/third-party/tocbot.js +0 -0
  150. package/docs/sidechat.js/2.6.4/scripts/third-party/tocbot.min.js +0 -0
  151. package/docs/sidechat.js/2.6.4/styles/clean-jsdoc-theme-base.css +0 -0
  152. package/docs/sidechat.js/2.6.4/styles/clean-jsdoc-theme-dark.css +0 -0
  153. package/docs/sidechat.js/2.6.4/styles/clean-jsdoc-theme-light.css +0 -0
  154. package/docs/sidechat.js/2.6.4/styles/clean-jsdoc-theme-scrollbar.css +0 -0
  155. package/docs/sidechat.js/2.6.4/styles/clean-jsdoc-theme-without-scrollbar.min.css +0 -0
  156. package/docs/sidechat.js/2.6.4/styles/clean-jsdoc-theme.min.css +0 -0
  157. package/docs/sidechat.js/2.6.4/tutorial-Authentication.html +0 -0
  158. package/docs/sidechat.js/2.6.4/tutorial-Email Registration.html +0 -0
  159. package/docs/sidechat.js/2.6.4/types_SidechatTypes.js.html +0 -0
  160. package/docs/sidechat.js/2.6.5/SidechatAPIClient.html +3 -0
  161. package/docs/sidechat.js/2.6.5/SidechatAPIError.html +3 -0
  162. package/docs/sidechat.js/2.6.5/SidechatColorList.html +3 -0
  163. package/docs/sidechat.js/2.6.5/classes_SidechatAPIClient.js.html +1429 -0
  164. package/docs/sidechat.js/2.6.5/classes_SidechatAPIError.js.html +16 -0
  165. package/docs/sidechat.js/2.6.5/classes_SidechatColorList.js.html +79 -0
  166. package/docs/sidechat.js/2.6.5/data/search.json +1 -0
  167. package/docs/sidechat.js/2.6.5/fonts/Inconsolata-Regular.ttf +0 -0
  168. package/docs/sidechat.js/2.6.5/fonts/OpenSans-Regular.ttf +0 -0
  169. package/docs/sidechat.js/2.6.5/fonts/WorkSans-Bold.ttf +0 -0
  170. package/docs/sidechat.js/2.6.5/global.html +3 -0
  171. package/docs/sidechat.js/2.6.5/index.html +4 -0
  172. package/docs/sidechat.js/2.6.5/scripts/core.js +726 -0
  173. package/docs/sidechat.js/2.6.5/scripts/core.min.js +23 -0
  174. package/docs/sidechat.js/2.6.5/scripts/resize.js +90 -0
  175. package/docs/sidechat.js/2.6.5/scripts/search.js +265 -0
  176. package/docs/sidechat.js/2.6.5/scripts/search.min.js +6 -0
  177. package/docs/sidechat.js/2.6.5/scripts/third-party/Apache-License-2.0.txt +202 -0
  178. package/docs/sidechat.js/2.6.5/scripts/third-party/fuse.js +9 -0
  179. package/docs/sidechat.js/2.6.5/scripts/third-party/hljs-line-num-original.js +369 -0
  180. package/docs/sidechat.js/2.6.5/scripts/third-party/hljs-line-num.js +1 -0
  181. package/docs/sidechat.js/2.6.5/scripts/third-party/hljs-original.js +5171 -0
  182. package/docs/sidechat.js/2.6.5/scripts/third-party/hljs.js +1 -0
  183. package/docs/sidechat.js/2.6.5/scripts/third-party/popper.js +5 -0
  184. package/docs/sidechat.js/2.6.5/scripts/third-party/tippy.js +1 -0
  185. package/docs/sidechat.js/2.6.5/scripts/third-party/tocbot.js +672 -0
  186. package/docs/sidechat.js/2.6.5/scripts/third-party/tocbot.min.js +1 -0
  187. package/docs/sidechat.js/2.6.5/styles/clean-jsdoc-theme-base.css +1159 -0
  188. package/docs/sidechat.js/2.6.5/styles/clean-jsdoc-theme-dark.css +412 -0
  189. package/docs/sidechat.js/2.6.5/styles/clean-jsdoc-theme-light.css +482 -0
  190. package/docs/sidechat.js/2.6.5/styles/clean-jsdoc-theme-scrollbar.css +30 -0
  191. package/docs/sidechat.js/2.6.5/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
  192. package/docs/sidechat.js/2.6.5/styles/clean-jsdoc-theme.min.css +1 -0
  193. package/docs/sidechat.js/2.6.5/tutorial-Authentication.html +14 -0
  194. package/docs/sidechat.js/2.6.5/tutorial-Email Registration.html +6 -0
  195. package/docs/sidechat.js/2.6.5/types_SidechatTypes.js.html +204 -0
  196. package/docs/sidechat.js/2.6.6/SidechatAPIClient.html +3 -0
  197. package/docs/sidechat.js/2.6.6/SidechatAPIError.html +3 -0
  198. package/docs/sidechat.js/2.6.6/SidechatColorList.html +3 -0
  199. package/docs/sidechat.js/2.6.6/classes_SidechatAPIClient.js.html +1447 -0
  200. package/docs/sidechat.js/2.6.6/classes_SidechatAPIError.js.html +16 -0
  201. package/docs/sidechat.js/2.6.6/classes_SidechatColorList.js.html +79 -0
  202. package/docs/sidechat.js/2.6.6/data/search.json +1 -0
  203. package/docs/sidechat.js/2.6.6/fonts/Inconsolata-Regular.ttf +0 -0
  204. package/docs/sidechat.js/2.6.6/fonts/OpenSans-Regular.ttf +0 -0
  205. package/docs/sidechat.js/2.6.6/fonts/WorkSans-Bold.ttf +0 -0
  206. package/docs/sidechat.js/2.6.6/global.html +3 -0
  207. package/docs/sidechat.js/2.6.6/index.html +4 -0
  208. package/docs/sidechat.js/2.6.6/scripts/core.js +726 -0
  209. package/docs/sidechat.js/2.6.6/scripts/core.min.js +23 -0
  210. package/docs/sidechat.js/2.6.6/scripts/resize.js +90 -0
  211. package/docs/sidechat.js/2.6.6/scripts/search.js +265 -0
  212. package/docs/sidechat.js/2.6.6/scripts/search.min.js +6 -0
  213. package/docs/sidechat.js/2.6.6/scripts/third-party/Apache-License-2.0.txt +202 -0
  214. package/docs/sidechat.js/2.6.6/scripts/third-party/fuse.js +9 -0
  215. package/docs/sidechat.js/2.6.6/scripts/third-party/hljs-line-num-original.js +369 -0
  216. package/docs/sidechat.js/2.6.6/scripts/third-party/hljs-line-num.js +1 -0
  217. package/docs/sidechat.js/2.6.6/scripts/third-party/hljs-original.js +5171 -0
  218. package/docs/sidechat.js/2.6.6/scripts/third-party/hljs.js +1 -0
  219. package/docs/sidechat.js/2.6.6/scripts/third-party/popper.js +5 -0
  220. package/docs/sidechat.js/2.6.6/scripts/third-party/tippy.js +1 -0
  221. package/docs/sidechat.js/2.6.6/scripts/third-party/tocbot.js +672 -0
  222. package/docs/sidechat.js/2.6.6/scripts/third-party/tocbot.min.js +1 -0
  223. package/docs/sidechat.js/2.6.6/styles/clean-jsdoc-theme-base.css +1159 -0
  224. package/docs/sidechat.js/2.6.6/styles/clean-jsdoc-theme-dark.css +412 -0
  225. package/docs/sidechat.js/2.6.6/styles/clean-jsdoc-theme-light.css +482 -0
  226. package/docs/sidechat.js/2.6.6/styles/clean-jsdoc-theme-scrollbar.css +30 -0
  227. package/docs/sidechat.js/2.6.6/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
  228. package/docs/sidechat.js/2.6.6/styles/clean-jsdoc-theme.min.css +1 -0
  229. package/docs/sidechat.js/2.6.6/tutorial-Authentication.html +14 -0
  230. package/docs/sidechat.js/2.6.6/tutorial-Email Registration.html +6 -0
  231. package/docs/sidechat.js/2.6.6/types_SidechatTypes.js.html +204 -0
  232. package/docs/tutorials/Authentication.md +0 -0
  233. package/docs/tutorials/Email Registration.md +0 -0
  234. package/jsdoc.json +0 -0
  235. package/package.json +1 -1
  236. package/rollup.config.mjs +0 -0
  237. package/src/classes/SidechatAPIClient.js +1444 -1414
  238. package/src/classes/SidechatAPIError.js +0 -0
  239. package/src/classes/SidechatColorList.js +0 -0
  240. package/src/index.js +0 -0
  241. package/src/tests/colorTest.js +0 -0
  242. package/src/tests/commentTest.js +0 -0
  243. package/src/tests/directMessageTest.js +0 -0
  244. package/src/tests/logInTest.js +0 -0
  245. package/src/tests/postTest.js +0 -0
  246. package/src/tests/updatesTest.js +0 -0
  247. package/src/types/SidechatTypes.js +0 -0
  248. package/tsconfig.json +0 -0
package/dist/index.mjs CHANGED
@@ -213,1414 +213,1445 @@ class SidechatAPIError extends Error {
213
213
  }
214
214
  }
215
215
 
216
- /**
217
- * API client class for making requests to Sidechat's private API. You'll need to [authenticate]{@tutorial Authentication} before using most of the methods.
218
- * @class
219
- * @since 2.0.0-alpha.0
220
- */
221
- class SidechatAPIClient {
222
- /**
223
- * User bearer token
224
- * @type {SidechatAuthToken}
225
- * */
226
- userToken;
227
-
228
- /**
229
- * Default headers for every API request
230
- * @type {Object}
231
- * @static
232
- * @constant
233
- */
234
- defaultHeaders = {
235
- Accept: "application/json",
236
- "Content-Type": "application/json",
237
- "App-Version": "6.0.0",
238
- Dnt: 1,
239
- };
240
-
241
- /**
242
- * Root URL for every API request
243
- * @type {String}
244
- * @default "https://api.sidechat.lol"
245
- */
246
- apiRoot = "https://api.sidechat.lol";
247
-
248
- /**
249
- * Create a new instance of the API client
250
- * @param {SidechatAuthToken} [token] - user bearer token
251
- * @param {String} rootUrl - custom API root URL for mocking or using other server
252
- */
253
- constructor(token = "", rootUrl = "") {
254
- if (token) {
255
- this.userToken = token;
256
- }
257
- if (rootUrl) {
258
- this.apiRoot = rootUrl;
259
- }
260
- }
261
-
262
- /**
263
- * Manually set the currently signed in user's token. Generally try to avoid this and instead either pass a token to the constructor or login automatically through the auth functions
264
- * @method
265
- * @param {SidechatAuthToken} token - user bearer token
266
- */
267
- setToken = (token) => {
268
- this.userToken = token;
269
- };
270
-
271
- /**
272
- * Manually set the root URL for all API requests. This can be used for mocking requests or redirecting them to a different server
273
- * @method
274
- * @param {String} url - new root URL to set
275
- * @since 2.3.9
276
- */
277
- setAPIRoot = (url) => {
278
- this.apiRoot = url;
279
- };
280
-
281
- /**
282
- * Run an arbitrary API request using the current client's authentication
283
- * @method
284
- * @param {String} endpoint - API endpoint to request (e.g. "/v1/posts")
285
- * @param {"GET"|"POST"|"PUT"|"DELETE"|"PATCH"|"OPTIONS"} [method] - HTTP method to use
286
- * @param {Object} [body] - body to send with the request
287
- * @param {Object} [headers] - custom headers to send with the request
288
- * @param {Boolean} [stripHeaders] - remove the default headers from the request
289
- * @since 2.4.9
290
- */
291
- sendRequest = (
292
- endpoint,
293
- method = "GET",
294
- body = undefined,
295
- headers = {},
296
- stripHeaders = false
297
- ) => {
298
- let requestHeaders = this.defaultHeaders;
299
- if (stripHeaders) {
300
- headers = {};
301
- }
302
- requestHeaders = { ...requestHeaders, ...headers };
303
- return fetch(`${this.apiRoot}${endpoint}`, {
304
- headers: { Authorization: `Bearer ${this.userToken}`, ...requestHeaders },
305
- body: body,
306
- method: method,
307
- });
308
- };
309
-
310
- /**
311
- * Initiate the login process with a phone number. Should be followed up with verifySMSCode().
312
- * @method
313
- * @since 1.0.0
314
- * @param {Number} phoneNumber - US phone number (WITHOUT +1) to send verification code to
315
- */
316
- loginViaSMS = async (phoneNumber) => {
317
- try {
318
- const res = await fetch(`${this.apiRoot}/v1/login_register`, {
319
- method: "POST",
320
- headers: this.defaultHeaders,
321
- body: JSON.stringify({
322
- phone_number: `+1${phoneNumber}`,
323
- version: 3,
324
- }),
325
- });
326
- const json = await res.json();
327
- return json;
328
- } catch (err) {
329
- console.error(err);
330
- throw new SidechatAPIError("Failed to request SMS verification.");
331
- }
332
- };
333
-
334
- /**
335
- * Verify the code sent via SMS with loginViaSMS(). If this function succeeds, the user will be authenticated for future requests.
336
- * @method
337
- * @since 1.0.0
338
- * @param {Number} phoneNumber - US phone number (WITHOUT +1) that verification code was sent to
339
- * @param {String} code - the verification code
340
- */
341
- verifySMSCode = async (phoneNumber, code) => {
342
- try {
343
- const res = await fetch(`${this.apiRoot}/v1/verify_phone_number`, {
344
- method: "POST",
345
- headers: this.defaultHeaders,
346
- body: JSON.stringify({
347
- phone_number: `+1${phoneNumber}`,
348
- code: code.toUpperCase(),
349
- }),
350
- });
351
- const json = await res.json();
352
- if (json?.logged_in_user?.token) {
353
- this.userToken = json.logged_in_user.token;
354
- }
355
- return json;
356
- } catch (err) {
357
- console.error(err);
358
- throw new SidechatAPIError("Failed verify this code.");
359
- }
360
- };
361
-
362
- /**
363
- * Set the user's age. If this function succeeds, the user will be authenticated for future requests.
364
- * @method
365
- * @since 1.0.0
366
- * @param {Number} age - user's age in years
367
- * @param {String} registrationID - the registration ID generated by verifySMSCode()
368
- */
369
- setAge = async (age, registrationID) => {
370
- if (age < 13) {
371
- throw new SidechatAPIError("You're too young to use Offsides.");
372
- }
373
- try {
374
- const res = await fetch(`${this.apiRoot}/v1/complete_registration`, {
375
- method: "POST",
376
- headers: this.defaultHeaders,
377
- body: JSON.stringify({
378
- age: Number(age),
379
- registration_id: registrationID,
380
- }),
381
- });
382
- const json = await res.json();
383
- if (json.token) {
384
- this.userToken = json.token;
385
- }
386
- return json;
387
- } catch (err) {
388
- console.error(err);
389
- throw new SidechatAPIError("Failed verify this code.");
390
- }
391
- };
392
-
393
- /**
394
- * Initiate the email setup process. Should be followed up with checkEmailVerification().
395
- * @method
396
- * @since 1.0.0
397
- * @param {String} email - school email address to send verification code to
398
- * @tutorial Email Registration
399
- */
400
- registerEmail = async (email) => {
401
- if (!this.userToken) {
402
- throw new SidechatAPIError("User is not authenticated.");
403
- }
404
- try {
405
- const res = await fetch(`${this.apiRoot}/v2/users/register_email`, {
406
- method: "POST",
407
- headers: {
408
- ...this.defaultHeaders,
409
- Authorization: `Bearer ${this.userToken}`,
410
- },
411
- body: JSON.stringify({
412
- email: email,
413
- }),
414
- });
415
- const json = await res.json();
416
- if (json.message) {
417
- throw new SidechatAPIError(json.message);
418
- }
419
- return json;
420
- } catch (err) {
421
- console.error(err);
422
- throw new SidechatAPIError("Failed to request email verification.");
423
- }
424
- };
425
-
426
- /**
427
- * Check is the user's email is verified.
428
- * @method
429
- * @since 1.0.0
430
- */
431
- checkEmailVerification = async () => {
432
- if (!this.userToken) {
433
- throw new SidechatAPIError("User is not authenticated.");
434
- }
435
- try {
436
- const res = await fetch(`${this.apiRoot}/v1/users/check_email_verified`, {
437
- method: "GET",
438
- headers: {
439
- ...this.defaultHeaders,
440
- Authorization: `Bearer ${this.userToken}`,
441
- },
442
- });
443
- const json = await res.json();
444
- if (json.verified_email_updates_response) {
445
- return json.verified_email_updates_response;
446
- } else if (json.changing_phone_number_verified_email_user) {
447
- return json.changing_phone_number_verified_email_user;
448
- } else {
449
- throw new SidechatAPIError(json?.message || "Email is not verified.");
450
- }
451
- } catch (err) {
452
- console.error(err);
453
- throw new SidechatAPIError("Email is not verified.");
454
- }
455
- };
456
-
457
- /**
458
- * Set the device ID of the current user
459
- * @method
460
- * @since 1.0.0
461
- * @param {String} deviceId - the device ID to set
462
- */
463
- setDeviceID = async (deviceID) => {
464
- if (!this.userToken) {
465
- throw new SidechatAPIError("User is not authenticated.");
466
- }
467
- try {
468
- const res = await fetch(`${this.apiRoot}/v1/register_device_token`, {
469
- method: "POST",
470
- headers: {
471
- ...this.defaultHeaders,
472
- Authorization: `Bearer ${this.userToken}`,
473
- },
474
- body: JSON.stringify({
475
- build_type: "release",
476
- bundle_id: "com.flowerave.sidechat",
477
- device_token: deviceID,
478
- }),
479
- });
480
- const json = await res.json();
481
- return json;
482
- } catch (err) {
483
- console.error(err);
484
- throw new SidechatAPIError("Failed verify this code.");
485
- }
486
- };
487
-
488
- /**
489
- * Get updated status for user and group
490
- * @method
491
- * @since 1.0.0
492
- * @deprecated since 2.1.0, will be removed by 3.0.0. Please use `getUpdates` instead!
493
- * @param {String} [groupID] - ID of a specific group to retrieve info from. Falls back to user's primary group.
494
- */
495
- getUserAndGroup = async (groupID = "") => {
496
- const json = await this.getUpdates(groupID);
497
- return json;
498
- };
499
-
500
- /**
501
- * Get updated status for user and group
502
- * @method
503
- * @since 2.1.0
504
- * @param {String} [groupID] - ID of a specific group to retrieve info from. Falls back to user's primary group.
505
- */
506
- getUpdates = async (groupID = "") => {
507
- if (!this.userToken) {
508
- throw new SidechatAPIError("User is not authenticated.");
509
- }
510
- try {
511
- const res = await fetch(
512
- `${this.apiRoot}/v1/updates?group_id=${groupID}`,
513
- {
514
- method: "GET",
515
- headers: {
516
- ...this.defaultHeaders,
517
- Authorization: `Bearer ${this.userToken}`,
518
- },
519
- }
520
- );
521
- const json = await res.json();
522
- return json;
523
- } catch (err) {
524
- console.error(err);
525
- throw new SidechatAPIError(`Failed to get posts from group.`);
526
- }
527
- };
528
-
529
- /**
530
- * Fetches posts from the specified category in a group
531
- * @method
532
- * @since 1.0.0
533
- * @param {String} groupID - group ID
534
- * @param {"hot"|"recent"|"top"} category - category to filter posts
535
- * @param {SidechatCursorString} [cursor] - cursor string
536
- * @returns {SidechatPostsAndCursor} List of posts and cursor
537
- */
538
- getGroupPosts = async (groupID, category = "hot", cursor) => {
539
- if (!this.userToken) {
540
- throw new SidechatAPIError("User is not authenticated.");
541
- }
542
- try {
543
- const res = await fetch(
544
- `${this.apiRoot}/v1/posts?${cursor ? "cursor=" + encodeURIComponent(cursor) + "&" : ""
545
- }group_id=${groupID}&type=${category}`,
546
- {
547
- method: "GET",
548
- headers: {
549
- ...this.defaultHeaders,
550
- Authorization: `Bearer ${this.userToken}`,
551
- },
552
- }
553
- );
554
- const json = await res.json();
555
- return json;
556
- } catch (err) {
557
- console.error(err);
558
- throw new SidechatAPIError(`Failed to get posts from group.`);
559
- }
560
- };
561
-
562
- /**
563
- * Upvote or downvote, or unvote a post
564
- * @method
565
- * @since 2.0.0-alpha.0
566
- * @param {String} postID - post ID to vote on
567
- * @param {SidechatVoteString} action - whether to upvote, downvote, or reset vote
568
- */
569
- setVote = async (postID, action) => {
570
- if (!this.userToken) {
571
- throw new SidechatAPIError("User is not authenticated.");
572
- }
573
- try {
574
- const res = await fetch(`${this.apiRoot}/v1/posts/set_vote`, {
575
- method: "POST",
576
- headers: {
577
- ...this.defaultHeaders,
578
- Authorization: `Bearer ${this.userToken}`,
579
- },
580
- body: JSON.stringify({
581
- post_id: postID,
582
- vote_status: action,
583
- }),
584
- });
585
- const json = await res.json();
586
- return json;
587
- } catch (err) {
588
- console.error(err);
589
- throw new SidechatAPIError(`Failed to change the vote on post.`);
590
- }
591
- };
592
-
593
- /**
594
- * Fetches a single post with just its ID
595
- * @method
596
- * @since 2.3.0
597
- * @param {String} postID - ID of post to fetch
598
- * @param {Boolean} includeDeleted - undocumented
599
- * @returns {SidechatPostOrComment} post object
600
- */
601
- getPost = async (postID, includeDeleted = false) => {
602
- if (!this.userToken) {
603
- throw new SidechatAPIError("User is not authenticated.");
604
- }
605
- try {
606
- const res = await fetch(
607
- `${this.apiRoot}/v1/posts/get?include_deleted=${includeDeleted}&post_id=${postID}`,
608
- {
609
- method: "GET",
610
- headers: {
611
- ...this.defaultHeaders,
612
- Authorization: `Bearer ${this.userToken}`,
613
- },
614
- }
615
- );
616
- const json = await res.json();
617
- return json.post;
618
- } catch (err) {
619
- console.error(err);
620
- throw new SidechatAPIError(`Failed to get post from ID.`);
621
- }
622
- };
623
-
624
- /**
625
- * Fetches the posts or comments that the user has created
626
- * @method
627
- * @since 2.3.5
628
- * @param {"posts"|"comments"} contentType - type of user content to fetch
629
- * @returns {SidechatPostOrComment[]} post object
630
- */
631
- getUserContent = async (contentType) => {
632
- if (!this.userToken) {
633
- throw new SidechatAPIError("User is not authenticated.");
634
- }
635
- if (contentType == "posts") {
636
- contentType = "my_posts";
637
- } else if (contentType == "comments") {
638
- contentType = "my_comments";
639
- }
640
- try {
641
- const res = await fetch(`${this.apiRoot}/v1/posts&type=${contentType}`, {
642
- method: "GET",
643
- headers: {
644
- ...this.defaultHeaders,
645
- Authorization: `Bearer ${this.userToken}`,
646
- },
647
- });
648
- const json = await res.json();
649
- return json.posts;
650
- } catch (err) {
651
- console.error(err);
652
- throw new SidechatAPIError(`Failed to get content from user.`);
653
- }
654
- };
655
-
656
- /**
657
- * Get all the commments on a post
658
- * @method
659
- * @since 2.0.0-alpha.0
660
- * @param {String} postID - post ID to get comments for
661
- * @returns {SidechatPostOrComment[]} list of comments
662
- */
663
- getPostComments = async (postID) => {
664
- if (!this.userToken) {
665
- throw new SidechatAPIError("User is not authenticated.");
666
- }
667
- try {
668
- const res = await fetch(
669
- `${this.apiRoot}/v1/posts/comments/?post_id=${postID}`,
670
- {
671
- method: "GET",
672
- headers: {
673
- ...this.defaultHeaders,
674
- Authorization: `Bearer ${this.userToken}`,
675
- },
676
- }
677
- );
678
- const json = await res.json();
679
- // Function to preprocess the comments and organize them into a nested structure
680
- function preprocessComments(apiComments) {
681
- // Map to store comments by their IDs for efficient lookup
682
- const commentMap = new Map();
683
- // List to store top-level comments
684
- const topLevelComments = [];
685
-
686
- // Iterate through the API comments
687
- apiComments.forEach((comment) => {
688
- // Store the comment in the map with its ID as the key
689
- commentMap.set(comment.id, comment);
690
- // Get the parent comment using the reply_post_id
691
- const parentComment = commentMap.get(comment.reply_post_id);
692
- // Check if the comment is a top-level comment
693
- if (
694
- !parentComment ||
695
- comment.reply_post_id === comment.parent_post_id
696
- ) {
697
- // If it's a top-level comment, push it to the topLevelComments array
698
- topLevelComments.push(comment);
699
- } else {
700
- // If it's a reply, add it to the parent comment's replies array
701
- if (!parentComment.replies) parentComment.replies = [];
702
- parentComment.replies.push(comment);
703
- }
704
- });
705
-
706
- // Flatten the nested structure and return a single list of comments
707
- return flattenComments(topLevelComments);
708
- }
709
-
710
- // Function to flatten nested comments into a single list
711
- function flattenComments(comments) {
712
- // Use reduce to flatten the nested comments array into a single list
713
- return comments.reduce((flatComments, comment) => {
714
- // Push the current comment to the flatComments array
715
- flatComments.push(comment);
716
- // If the current comment has replies, recursively flatten them and push to the flatComments array
717
- if (comment.replies)
718
- flatComments.push(...flattenComments(comment.replies));
719
- // Return the flatComments array
720
- return flatComments;
721
- }, []);
722
- }
723
-
724
- const sortedComments = preprocessComments(json.posts);
725
- return sortedComments;
726
- } catch (err) {
727
- console.error(err);
728
- throw new SidechatAPIError(`Failed to get comments on post.`);
729
- }
730
- };
731
-
732
- /**
733
- * Gets groups to be displayed on the "Explore Groups" page
734
- * @method
735
- * @since 2.0.0-alpha.0
736
- * @param {Boolean} onePage - whether or not results should be returned as a single page
737
- * @returns {SidechatGroup[]}
738
- */
739
- getAvailableGroups = async (onePage = true) => {
740
- if (!this.userToken) {
741
- throw new SidechatAPIError("User is not authenticated.");
742
- }
743
- try {
744
- const res = await fetch(`${this.apiRoot}/v1/groups/explore`, {
745
- method: "GET",
746
- headers: {
747
- ...this.defaultHeaders,
748
- "App-Version": onePage ? "0" : this.defaultHeaders["App-Version"],
749
- Authorization: `Bearer ${this.userToken}`,
750
- },
751
- });
752
- const json = await res.json();
753
- return await json.groups;
754
- } catch (err) {
755
- console.error(err);
756
- throw new SidechatAPIError(`Failed to get groups from explore.`);
757
- }
758
- };
759
-
760
- /**
761
- * Searches for new groups based on a query keyword
762
- * @method
763
- * @since 2.6.0
764
- * @param {String} query - the string to search for. This will be encoded, so strings with spaces and special characters are okay.
765
- * @returns {SidechatGroup[]}
766
- */
767
- searchAvailableGroups = async (query) => {
768
- if (!this.userToken) {
769
- throw new SidechatAPIError("User is not authenticated.");
770
- }
771
- try {
772
- const res = await fetch(`${this.apiRoot}/v1/groups/explore/search?term=${encodeURIComponent(query)}`, {
773
- method: "GET",
774
- headers: {
775
- ...this.defaultHeaders,
776
- Authorization: `Bearer ${this.userToken}`,
777
- },
778
- });
779
- const json = await res.json();
780
- return await json.results;
781
- } catch (err) {
782
- console.error(err);
783
- throw new SidechatAPIError(`Failed to search groups.`);
784
- }
785
- };
786
-
787
- /**
788
- * Retrieves the entire accessible asset library. Be warned that as of the time of this documentation, it's a 1.5MB JSON download and this request is very expensive.
789
- * @method
790
- * @since 2.0.6
791
- * @returns {SidechatLibraryAsset[]}
792
- */
793
- getAssetLibrary = async () => {
794
- if (!this.userToken) {
795
- throw new SidechatAPIError("User is not authenticated.");
796
- }
797
- try {
798
- const res = await fetch(`${this.apiRoot}/v1/assets/library`, {
799
- method: "GET",
800
- headers: {
801
- ...this.defaultHeaders,
802
- Authorization: `Bearer ${this.userToken}`,
803
- },
804
- });
805
- const json = await res.json();
806
- return await json.items;
807
- } catch (err) {
808
- console.error(err);
809
- throw new SidechatAPIError(`Failed to get asset library.`);
810
- }
811
- };
812
-
813
- /**
814
- * Gets the current authenticated user and a list of the groups of which they are members.
815
- * @method
816
- * @since 2.1.0
817
- * @returns {SidechatCurrentUser}
818
- */
819
- getCurrentUser = async () => {
820
- if (!this.userToken) {
821
- throw new SidechatAPIError("User is not authenticated.");
822
- }
823
- try {
824
- const res = await fetch(`${this.apiRoot}/v1/users/me`, {
825
- method: "GET",
826
- headers: {
827
- ...this.defaultHeaders,
828
- Authorization: `Bearer ${this.userToken}`,
829
- },
830
- });
831
- const json = await res.json();
832
- return await json;
833
- } catch (err) {
834
- console.error(err);
835
- throw new SidechatAPIError(`Failed to get asset library.`);
836
- }
837
- };
838
-
839
- /**
840
- * Gets the metadata of a group from its ID
841
- * @method
842
- * @since 2.1.0
843
- * @param {String} [groupID] - alphanumeric ID of a group to get. Falls back to user's primary group.
844
- * @returns {SidechatGroup}
845
- */
846
- getGroupMetadata = async (groupID = "") => {
847
- if (!this.userToken) {
848
- throw new SidechatAPIError("User is not authenticated.");
849
- }
850
- try {
851
- const res = await fetch(`${this.apiRoot}/v1/groups/${groupID}`, {
852
- method: "GET",
853
- headers: {
854
- ...this.defaultHeaders,
855
- Authorization: `Bearer ${this.userToken}`,
856
- },
857
- });
858
- const json = await res.json();
859
- return await json.group;
860
- } catch (err) {
861
- console.error(err);
862
- throw new SidechatAPIError(`Failed to get group metadata.`);
863
- }
864
- };
865
-
866
- /**
867
- * Joins or leaves a group
868
- * @method
869
- * @param {String} groupID - alphanumeric ID of group to join or leave
870
- * @param {Boolean} isMember - whether or not the user should be a member of the group
871
- * @since 2.3.8
872
- */
873
- setGroupMembership = async (groupID, isMember) => {
874
- if (!this.userToken) {
875
- throw new SidechatAPIError("User is not authenticated.");
876
- }
877
- try {
878
- const res = await fetch(
879
- `${this.apiRoot}/v1/groups/${isMember ? "join" : "leave"}`,
880
- {
881
- method: "POST",
882
- headers: {
883
- ...this.defaultHeaders,
884
- Authorization: `Bearer ${this.userToken}`,
885
- },
886
- body: JSON.stringify({
887
- group_id: groupID,
888
- }),
889
- }
890
- );
891
- const json = await res.json();
892
- return await json;
893
- } catch (err) {
894
- console.error(err);
895
- throw new SidechatAPIError(`Failed to modify group membership.`);
896
- }
897
- };
898
-
899
- /**
900
- * Creates a comment on a post
901
- * @method
902
- * @since 2.2.0
903
- * @param {String} parentPostID - alphanumeric ID of post on which this comment is made
904
- * @param {String} text - text content of comment
905
- * @param {String} groupID - alphanumeric ID of group in which the parent post resides
906
- * @param {String} [replyCommentID] - alphanumeric ID of comment to reply to. Falls back to parentPostID
907
- * @param {String} [topLevelReplyID] - alphanumeric ID of the top-level comment to reply to. Used only when replying to replies. Falls back to parentPostID
908
- * @param {SidechatSimpleAsset[]} [assetList] - list of assets to attach
909
- * @param {Boolean} [disableDMs] - prevent direct messages being sent to comment's author
910
- * @param {Boolean} [anonymous] - whether or not to hide user's name and icon on comment
911
- * @returns {SidechatPostOrComment} created comment
912
- */
913
- createComment = async (
914
- parentPostID,
915
- text,
916
- groupID,
917
- replyCommentID,
918
- topLevelReplyID,
919
- assetList = [],
920
- disableDMs = false,
921
- anonymous = false
922
- ) => {
923
- if (!this.userToken) {
924
- throw new SidechatAPIError("User is not authenticated.");
925
- }
926
- try {
927
- const res = await fetch(`${this.apiRoot}/v1/posts`, {
928
- method: "POST",
929
- headers: {
930
- ...this.defaultHeaders,
931
- Authorization: `Bearer ${this.userToken}`,
932
- },
933
- body: JSON.stringify({
934
- type: "comment",
935
- assets: assetList,
936
- group_ids: [groupID],
937
- text: text,
938
- reply_post_id: topLevelReplyID || replyCommentID || parentPostID,
939
- reply_comment_post_id: replyCommentID || parentPostID,
940
- parent_post_id: parentPostID,
941
- dms_disabled: disableDMs,
942
- using_identity: !anonymous,
943
- }),
944
- });
945
- const json = await res.json();
946
- return await json.comment;
947
- } catch (err) {
948
- console.error(err);
949
- throw new SidechatAPIError(`Failed to post comment.`);
950
- }
951
- };
952
-
953
- /**
954
- * Creates a new post in the specified group
955
- * @method
956
- * @since 2.2.0
957
- * @param {String} text - text content of comment
958
- * @param {String} groupID - alphanumeric ID of group in which the parent post resides
959
- * @param {SidechatSimpleAsset[]} [assetList] - list of assets to attach.
960
- * @param {Boolean} [disableDMs] - prevent direct messages from being sent to post's author
961
- * @param {Boolean} [disableComments] - whether or not comments should be disabled on post
962
- * @param {Boolean} [anonymous] - whether or not to hide user's name and icon on post
963
- * @returns {SidechatPostOrComment} the created post
964
- */
965
- createPost = async (
966
- text,
967
- groupID,
968
- assetList = [],
969
- disableDMs = false,
970
- disableComments = false,
971
- anonymous = false,
972
- repostId = undefined
973
- ) => {
974
- if (!this.userToken) {
975
- throw new SidechatAPIError("User is not authenticated.");
976
- }
977
- try {
978
- const res = await fetch(`${this.apiRoot}/v1/posts`, {
979
- method: "POST",
980
- headers: {
981
- ...this.defaultHeaders,
982
- Authorization: `Bearer ${this.userToken}`,
983
- },
984
- body: JSON.stringify({
985
- type: "post",
986
- assets: assetList,
987
- group_ids: [groupID],
988
- text: text,
989
- attachments: [],
990
- dms_disabled: disableDMs,
991
- comments_disabled: disableComments,
992
- using_identity: !anonymous,
993
- quote_post_id: repostId
994
- }),
995
- });
996
- const json = await res.json();
997
- return await json.posts[0];
998
- } catch (err) {
999
- console.error(err);
1000
- throw new SidechatAPIError(`Failed to make post.`);
1001
- }
1002
- };
1003
-
1004
- /**
1005
- * Deletes a post or comment that the user created
1006
- * @method
1007
- * @since 2.2.0
1008
- * @param {String} postOrCommentID - alphanumeric ID of post to delete
1009
- */
1010
- deletePostOrComment = async (postOrCommentID) => {
1011
- if (!this.userToken) {
1012
- throw new SidechatAPIError("User is not authenticated.");
1013
- }
1014
- try {
1015
- const res = await fetch(`${this.apiRoot}/v1/posts/delete`, {
1016
- method: "POST",
1017
- headers: {
1018
- ...this.defaultHeaders,
1019
- Authorization: `Bearer ${this.userToken}`,
1020
- },
1021
- body: JSON.stringify({
1022
- post_id: postOrCommentID,
1023
- }),
1024
- });
1025
- const json = await res.json();
1026
- return await json;
1027
- } catch (err) {
1028
- console.error(err);
1029
- throw new SidechatAPIError(`Failed to delete post.`);
1030
- }
1031
- };
1032
-
1033
- /**
1034
- * Votes on a poll attached to a post
1035
- * @method
1036
- * @param {String} pollId - alphanumeric ID of poll to vote on
1037
- * @param {Number} choiceIndex - index of the choice to vote on
1038
- * @since 2.5.4
1039
- */
1040
- voteOnPoll = async (pollId, choiceIndex) => {
1041
- if (!this.userToken) {
1042
- throw new SidechatAPIError("User is not authenticated.");
1043
- }
1044
- try {
1045
- const res = await fetch(`${this.apiRoot}/v1/polls/vote`, {
1046
- method: "POST",
1047
- headers: {
1048
- ...this.defaultHeaders,
1049
- Authorization: `Bearer ${this.userToken}`,
1050
- },
1051
- body: JSON.stringify({
1052
- poll_id: pollId,
1053
- choice: choiceIndex,
1054
- }),
1055
- });
1056
- const json = await res.json();
1057
- return await json;
1058
- } catch (err) {
1059
- console.error(err);
1060
- throw new SidechatAPIError(`Failed to vote on poll`);
1061
- }
1062
- };
1063
-
1064
- /**
1065
- * Marks that the user has viewed results on a poll. Note that this does not actually return the results of the poll.
1066
- * @method
1067
- * @param {String} pollId - alphanumeric ID of poll to vote on
1068
- * @since 2.5.4
1069
- */
1070
- viewPollResults = async (pollId) => {
1071
- if (!this.userToken) {
1072
- throw new SidechatAPIError("User is not authenticated.");
1073
- }
1074
- try {
1075
- const res = await fetch(`${this.apiRoot}/v1/polls/view_results`, {
1076
- method: "POST",
1077
- headers: {
1078
- ...this.defaultHeaders,
1079
- Authorization: `Bearer ${this.userToken}`,
1080
- },
1081
- body: JSON.stringify({
1082
- poll_id: pollId,
1083
- }),
1084
- });
1085
- const json = await res.json();
1086
- return await json;
1087
- } catch (err) {
1088
- console.error(err);
1089
- throw new SidechatAPIError(`Failed to mark poll results as viewed.`);
1090
- }
1091
- };
1092
-
1093
- /**
1094
- * Uploads an asset to AWS S3 for use in posts and comments. Currently photos only
1095
- * @method
1096
- * @param {String} uri - URI of the asset to upload
1097
- * @param {String} mimeType - mimetype of the asset (e.g. "image/png")
1098
- * @param {String} [name] - filename of the asset
1099
- * @returns {String} URL of the uploaded asset
1100
- * @since 2.5.1
1101
- */
1102
- uploadAsset = async (uri, mimeType, name = "") => {
1103
- if (!this.userToken) {
1104
- throw new SidechatAPIError("User is not authenticated.");
1105
- }
1106
-
1107
- let imageType = mimeType.split("/")[1];
1108
- if (!["png", "jpeg", "gif"].includes(imageType)) {
1109
- throw new SidechatAPIError("Unsupported image format.");
1110
- }
1111
-
1112
- const data = new FormData();
1113
- data.append("image", {
1114
- name: name,
1115
- type: mimeType,
1116
- uri: uri,
1117
- });
1118
-
1119
- const urlReq = await this.sendRequest(`/v1/assets/upload_url?content_type=${imageType}`);
1120
- const urlJson = await urlReq.json();
1121
-
1122
- try {
1123
- const uploadReq = await fetch(urlJson.upload_url, {
1124
- body: data.getAll("image")[0],
1125
- method: "PUT",
1126
- headers: {
1127
- "Content-Type": mimeType,
1128
- },
1129
- });
1130
- if (uploadReq.status == 200) {
1131
- return `${this.apiRoot}/v1/assets/library/${urlJson.asset_id}`;
1132
- } else {
1133
- throw new SidechatAPIError(
1134
- `Couldn't upload image - error ${uploadReq.status}`
1135
- );
1136
- }
1137
- } catch (e) {
1138
- throw new SidechatAPIError(e.message);
1139
- }
1140
- };
1141
-
1142
- /**
1143
- * Sets the conversation icon of a user
1144
- * @method
1145
- * @since 2.2.1
1146
- * @param {String} userID - alphanumeric ID of user
1147
- * @param {String} emoji - emoji to set as icon
1148
- * @param {String} primaryColor - hex string (including #) of primary color
1149
- * @param {String} secondaryColor - hex string (including #) of secondary color
1150
- */
1151
- setUserIcon = async (userID, emoji, primaryColor, secondaryColor) => {
1152
- if (!this.userToken) {
1153
- throw new SidechatAPIError("User is not authenticated.");
1154
- }
1155
- try {
1156
- const res = await fetch(`${this.apiRoot}/v1/users/${userID}`, {
1157
- method: "PATCH",
1158
- headers: {
1159
- ...this.defaultHeaders,
1160
- "App-Version": "0",
1161
- Authorization: `Bearer ${this.userToken}`,
1162
- },
1163
- body: JSON.stringify({
1164
- conversation_icon: {
1165
- emoji: emoji,
1166
- secondary_color: secondaryColor,
1167
- is_migrated: true,
1168
- color: primaryColor,
1169
- },
1170
- }),
1171
- });
1172
- const json = await res.json();
1173
- return await json;
1174
- } catch (err) {
1175
- console.error(err);
1176
- throw new SidechatAPIError(`Failed to set icon.`);
1177
- }
1178
- };
1179
-
1180
- /**
1181
- * Sets the bio text of a user
1182
- * @method
1183
- * @since 2.5.6
1184
- * @param {String} userID - alphanumeric ID of user
1185
- * @param {String} bio - text to set as bio
1186
- */
1187
- setUserBio = async (userID, bio) => {
1188
- if (!this.userToken) {
1189
- throw new SidechatAPIError("User is not authenticated.");
1190
- }
1191
- try {
1192
- const res = await fetch(`${this.apiRoot}/v1/users/${userID}`, {
1193
- method: "PATCH",
1194
- headers: {
1195
- ...this.defaultHeaders,
1196
- "App-Version": "5.4.22",
1197
- Authorization: `Bearer ${this.userToken}`,
1198
- },
1199
- body: JSON.stringify({
1200
- bio: bio
1201
- }),
1202
- });
1203
- const json = await res.json();
1204
- return await json;
1205
- } catch (err) {
1206
- console.error(err);
1207
- throw new SidechatAPIError(`Failed to set bio.`);
1208
- }
1209
- };
1210
-
1211
- /**
1212
- * Checks if user can set their username to a string
1213
- * @method
1214
- * @since 2.3.6
1215
- * @param {String} username - string to check
1216
- * @returns {Boolean} whether or not username is valid and unused
1217
- */
1218
- checkUsername = async (username) => {
1219
- if (!this.userToken) {
1220
- throw new SidechatAPIError("User is not authenticated.");
1221
- }
1222
- try {
1223
- const res = await fetch(
1224
- `${this.apiRoot}/v1/users/username?username=${username}`,
1225
- {
1226
- method: "GET",
1227
- headers: {
1228
- ...this.defaultHeaders,
1229
- Authorization: `Bearer ${this.userToken}`,
1230
- },
1231
- }
1232
- );
1233
- if (res?.status == 200 || res?.status == 204) {
1234
- return true;
1235
- } else {
1236
- return false;
1237
- }
1238
- } catch (err) {
1239
- console.error(err);
1240
- throw new SidechatAPIError(`Failed to check username.`);
1241
- }
1242
- };
1243
-
1244
- /**
1245
- * Changes the username of the current user
1246
- * @method
1247
- * @since 2.3.6
1248
- * @param {String} userID - alphanumeric ID of user
1249
- * @param {String} username - new username to set
1250
- */
1251
- setUsername = async (userID, username) => {
1252
- if (!this.userToken) {
1253
- throw new SidechatAPIError("User is not authenticated.");
1254
- }
1255
- try {
1256
- const res = await fetch(`${this.apiRoot}/v1/users/${userID}`, {
1257
- method: "PATCH",
1258
- headers: {
1259
- ...this.defaultHeaders,
1260
- Authorization: `Bearer ${this.userToken}`,
1261
- },
1262
- body: JSON.stringify({
1263
- username: username,
1264
- }),
1265
- });
1266
- const json = await res.json();
1267
- return await json.user;
1268
- } catch (err) {
1269
- console.error(err);
1270
- throw new SidechatAPIError(`Failed to set icon.`);
1271
- }
1272
- };
1273
-
1274
- /**
1275
- * Fetches a public user profile
1276
- * @method
1277
- * @since 2.6.0
1278
- * @param {String} username - username of the user to fetch
1279
- * @returns {SidechatProfile}
1280
- */
1281
- getUserProfile = async (username) => {
1282
- if (!this.userToken) {
1283
- throw new SidechatAPIError("User is not authenticated.");
1284
- }
1285
- try {
1286
- const res = await fetch(`${this.apiRoot}/v1/groups/username?username=${username}`, {
1287
- method: "GET",
1288
- headers: {
1289
- ...this.defaultHeaders,
1290
- Authorization: `Bearer ${this.userToken}`,
1291
- }
1292
- });
1293
- const json = await res.json();
1294
- return await json.group;
1295
- } catch (err) {
1296
- console.error(err);
1297
- throw new SidechatAPIError(`Failed to set icon.`);
1298
- }
1299
- };
1300
-
1301
- /**
1302
- * Fetches a public user's posts
1303
- * @method
1304
- * @since 2.6.0
1305
- * @param {String} username - username of the user to fetch
1306
- * @returns {SidechatPostOrComment[]}
1307
- */
1308
- getUserPosts = async (username) => {
1309
- if (!this.userToken) {
1310
- throw new SidechatAPIError("User is not authenticated.");
1311
- }
1312
- try {
1313
- const res = await fetch(`${this.apiRoot}/v1/users/posts?username=${username}`, {
1314
- method: "GET",
1315
- headers: {
1316
- ...this.defaultHeaders,
1317
- Authorization: `Bearer ${this.userToken}`,
1318
- }
1319
- });
1320
- const json = await res.json();
1321
- return await json.posts;
1322
- } catch (err) {
1323
- console.error(err);
1324
- throw new SidechatAPIError(`Failed to set icon.`);
1325
- }
1326
- };
1327
-
1328
- /**
1329
- * Marks an activity item as read
1330
- * @method
1331
- * @since 2.3.2
1332
- * @param {String} activityID - alphanumeric ID of activity object
1333
- */
1334
- readActivity = async (activityID) => {
1335
- if (!this.userToken) {
1336
- throw new SidechatAPIError("User is not authenticated.");
1337
- }
1338
- try {
1339
- const res = await fetch(`${this.apiRoot}/v1/activity/seen`, {
1340
- method: "POST",
1341
- headers: {
1342
- ...this.defaultHeaders,
1343
- Authorization: `Bearer ${this.userToken}`,
1344
- },
1345
- body: JSON.stringify({
1346
- ids: [activityID],
1347
- }),
1348
- });
1349
- const json = await res.json();
1350
- return await json;
1351
- } catch (err) {
1352
- console.error(err);
1353
- throw new SidechatAPIError(`Failed to mark activity as read.`);
1354
- }
1355
- };
1356
-
1357
- /**
1358
- * Retrieves joinable group chats
1359
- * @method
1360
- * @since 2.3.5
1361
- */
1362
- getGroupChats = async () => {
1363
- if (!this.userToken) {
1364
- throw new SidechatAPIError("User is not authenticated.");
1365
- }
1366
- try {
1367
- const res = await fetch(`${this.apiRoot}/v1/chats/explore`, {
1368
- method: "GET",
1369
- headers: {
1370
- ...this.defaultHeaders,
1371
- Authorization: `Bearer ${this.userToken}`,
1372
- },
1373
- });
1374
- const json = await res.json();
1375
- return await json.chats;
1376
- } catch (err) {
1377
- console.error(err);
1378
- throw new SidechatAPIError(`Failed to get groupchats.`);
1379
- }
1380
- };
1381
-
1382
- /**
1383
- * Joins a group chat. To mimic the official client, use the user's display name and icon by default.
1384
- * @method
1385
- * @param {String} groupChatID - alphanumeric ID of group chat to join
1386
- * @param {String} displayName - display name to use in chat
1387
- * @param {String} emoji - emoji to use as icon
1388
- * @param {String} primaryColor - hex string of primary color
1389
- * @param {String} secondaryColor - hex string of secondary color
1390
- * @since 2.3.5
1391
- */
1392
- joinGroupChat = async (
1393
- groupChatID,
1394
- displayName,
1395
- emoji,
1396
- primaryColor,
1397
- secondaryColor
1398
- ) => {
1399
- if (!this.userToken) {
1400
- throw new SidechatAPIError("User is not authenticated.");
1401
- }
1402
- try {
1403
- const res = await fetch(`${this.apiRoot}/v1/chats/groups/join`, {
1404
- method: "POST",
1405
- headers: {
1406
- ...this.defaultHeaders,
1407
- Authorization: `Bearer ${this.userToken}`,
1408
- },
1409
- body: JSON.stringify({
1410
- chat_id: groupChatID,
1411
- identity: {
1412
- display_name: displayName,
1413
- emoji: emoji,
1414
- secondary_color: secondaryColor,
1415
- color: primaryColor,
1416
- },
1417
- }),
1418
- });
1419
- const json = await res.json();
1420
- return await json;
1421
- } catch (err) {
1422
- console.error(err);
1423
- throw new SidechatAPIError(`Failed to join groupchat.`);
1424
- }
1425
- };
1426
-
1427
- /**
1428
- * Gets a list of the user's direct messages
1429
- * @method
1430
- * @returns {SidechatDirectThread[]}
1431
- * @since 2.4.4
1432
- */
1433
- getDMs = async () => {
1434
- if (!this.userToken) {
1435
- throw new SidechatAPIError("User is not authenticated.");
1436
- }
1437
- try {
1438
- const res = await fetch(`${this.apiRoot}/v1/chats`, {
1439
- method: "GET",
1440
- headers: {
1441
- ...this.defaultHeaders,
1442
- Authorization: `Bearer ${this.userToken}`,
1443
- },
1444
- });
1445
- const json = await res.json();
1446
- const list = await json.chats;
1447
- let r = [];
1448
- list.forEach((o) => {
1449
- r.push(o.chat);
1450
- });
1451
- return await r;
1452
- } catch (err) {
1453
- console.error(err);
1454
- throw new SidechatAPIError(`Failed to fetch DMs.`);
1455
- }
1456
- };
1457
-
1458
- /**
1459
- * Gets a single direct message thread
1460
- * @method
1461
- * @param {String} id - alphanumeric ID of the chat to fetch
1462
- * @returns {SidechatDirectThread}
1463
- * @since 2.4.4
1464
- */
1465
- getDMThread = async (id) => {
1466
- if (!this.userToken) {
1467
- throw new SidechatAPIError("User is not authenticated.");
1468
- }
1469
- try {
1470
- const res = await fetch(
1471
- `${this.apiRoot}/v1/chats/messages?chat_id=${id}`,
1472
- {
1473
- method: "GET",
1474
- headers: {
1475
- ...this.defaultHeaders,
1476
- Authorization: `Bearer ${this.userToken}`,
1477
- },
1478
- }
1479
- );
1480
- const json = await res.json();
1481
- return await json.chat;
1482
- } catch (err) {
1483
- console.error(err);
1484
- throw new SidechatAPIError(`Failed to fetch DM thread.`);
1485
- }
1486
- };
1487
-
1488
- /**
1489
- * Sends a message to an existing direct message thread - note that you must first use startDM() to start a thread.
1490
- * @method
1491
- * @param {String} chatID - alphanumeric ID of the chat to send to
1492
- * @param {String} text - text content of message
1493
- * @param {String} clientID - alphanumeric device ID
1494
- * @param {SidechatAsset[]} assets - array of assets to send
1495
- * @param {Boolean} anonymous - whether the DM should be sent anonymously
1496
- * @since 2.4.4
1497
- */
1498
- sendDM = async (chatID, text, clientID, assets = [], anonymous = false) => {
1499
- if (!this.userToken) {
1500
- throw new SidechatAPIError("User is not authenticated.");
1501
- }
1502
- try {
1503
- const res = await fetch(`${this.apiRoot}/v1/chats/send`, {
1504
- method: "POST",
1505
- headers: {
1506
- ...this.defaultHeaders,
1507
- Authorization: `Bearer ${this.userToken}`,
1508
- },
1509
- body: JSON.stringify({
1510
- chat_id: chatID,
1511
- text: text,
1512
- client_id: clientID,
1513
- anonymous: anonymous,
1514
- assets: assets,
1515
- }),
1516
- });
1517
- const json = await res.json();
1518
- return await json;
1519
- } catch (err) {
1520
- console.error(err);
1521
- throw new SidechatAPIError(`Failed to send message.`);
1522
- }
1523
- };
1524
-
1525
- /**
1526
- * Creates a new direct message thread
1527
- * @method
1528
- * @param {String} text - text content of message
1529
- * @param {String} clientID - alphanumeric ID of devide
1530
- * @param {String} postID - alphanumeric ID of post or comment
1531
- * @param {Boolean} anonymous - whether the DM should be sent anonymously
1532
- * @param {"feed"} postContext - context of post (mostly undocumented, defaults to "feed")
1533
- * @since 2.4.4
1534
- */
1535
- startDM = async (
1536
- text,
1537
- clientID,
1538
- postID,
1539
- anonymous = false,
1540
- postContext = "feed"
1541
- ) => {
1542
- if (!this.userToken) {
1543
- throw new SidechatAPIError("User is not authenticated.");
1544
- }
1545
- try {
1546
- const res = await fetch(`${this.apiRoot}/v1/chats/start`, {
1547
- method: "POST",
1548
- headers: {
1549
- ...this.defaultHeaders,
1550
- Authorization: `Bearer ${this.userToken}`,
1551
- },
1552
- body: JSON.stringify({
1553
- text: text,
1554
- client_id: clientID,
1555
- post_id: postID,
1556
- anonymous: anonymous,
1557
- post_context: postContext,
1558
- }),
1559
- });
1560
- const json = await res.json();
1561
- return await json;
1562
- } catch (err) {
1563
- console.error(err);
1564
- throw new SidechatAPIError(`Failed to start DM.`);
1565
- }
1566
- };
1567
-
1568
- /**
1569
- * Hides posts from user
1570
- * @method
1571
- * @param {String} postID - alphanumeric ID of post to hide
1572
- * @since 2.6.2
1573
- */
1574
- hidePostsFromUser = async (postID) => {
1575
- if (!this.userToken) {
1576
- throw new SidechatAPIError("User is not authenticated.");
1577
- }
1578
- try {
1579
- const res = await fetch(`${this.apiRoot}/v1/posts/hide_posts_from_user`, {
1580
- method: "POST",
1581
- headers: {
1582
- ...this.defaultHeaders,
1583
- Authorization: `Bearer ${this.userToken}`,
1584
- },
1585
- body: JSON.stringify({
1586
- post_id: postID,
1587
- post_context: "feed",
1588
- report: false
1589
- }),
1590
- });
1591
- const json = await res.json();
1592
- return await json;
1593
- } catch (err) {
1594
- console.error(err);
1595
- throw new SidechatAPIError(`Failed to hide post.`);
1596
- }
1597
- };
1598
-
1599
- /**
1600
- * Unhides all posts from all users
1601
- * @method
1602
- * @since 2.6.2
1603
- */
1604
- unhidePostsFromAllUsers = async () => {
1605
- if (!this.userToken) {
1606
- throw new SidechatAPIError("User is not authenticated.");
1607
- }
1608
- try {
1609
- const res = await fetch(`${this.apiRoot}/v1/users/unhide_all`, {
1610
- method: "POST",
1611
- headers: {
1612
- ...this.defaultHeaders,
1613
- Authorization: `Bearer ${this.userToken}`,
1614
- },
1615
- body: JSON.stringify({}),
1616
- });
1617
- const json = await res.json();
1618
- return await json;
1619
- } catch (err) {
1620
- console.error(err);
1621
- throw new SidechatAPIError(`Failed to unhide post.`);
1622
- }
1623
- };
216
+ /** @import * as types from "../types/SidechatTypes.js" */
217
+
218
+ /**
219
+ * API client class for making requests to Sidechat's private API. You'll need to [authenticate]{@tutorial Authentication} before using most of the methods.
220
+ * @class
221
+ * @since 2.0.0-alpha.0
222
+ */
223
+ class SidechatAPIClient {
224
+ /**
225
+ * User bearer token
226
+ * @type {types.SidechatAuthToken}
227
+ * */
228
+ userToken;
229
+
230
+ /**
231
+ * Default headers for every API request
232
+ * @type {Object}
233
+ * @static
234
+ * @constant
235
+ */
236
+ defaultHeaders = {
237
+ Accept: "application/json",
238
+ "Content-Type": "application/json",
239
+ "App-Version": "6.0.0",
240
+ Dnt: 1,
241
+ };
242
+
243
+ /**
244
+ * Root URL for every API request
245
+ * @type {String}
246
+ * @default "https://api.sidechat.lol"
247
+ */
248
+ apiRoot = "https://api.sidechat.lol";
249
+
250
+ /**
251
+ * Create a new instance of the API client
252
+ * @param {types.SidechatAuthToken} [token] - user bearer token
253
+ * @param {String} rootUrl - custom API root URL for mocking or using other server
254
+ */
255
+ constructor(token = "", rootUrl = "") {
256
+ if (token) {
257
+ this.userToken = token;
258
+ }
259
+ if (rootUrl) {
260
+ this.apiRoot = rootUrl;
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Manually set the currently signed in user's token. Generally try to avoid this and instead either pass a token to the constructor or login automatically through the auth functions
266
+ * @method
267
+ * @param {types.SidechatAuthToken} token - user bearer token
268
+ */
269
+ setToken = (token) => {
270
+ this.userToken = token;
271
+ };
272
+
273
+ /**
274
+ * Manually set the root URL for all API requests. This can be used for mocking requests or redirecting them to a different server
275
+ * @method
276
+ * @param {String} url - new root URL to set
277
+ * @since 2.3.9
278
+ */
279
+ setAPIRoot = (url) => {
280
+ this.apiRoot = url;
281
+ };
282
+
283
+ /**
284
+ * Run an arbitrary API request using the current client's authentication
285
+ * @method
286
+ * @param {String} endpoint - API endpoint to request (e.g. "/v1/posts")
287
+ * @param {"GET"|"POST"|"PUT"|"DELETE"|"PATCH"|"OPTIONS"} [method] - HTTP method to use
288
+ * @param {Object} [body] - body to send with the request
289
+ * @param {Object} [headers] - custom headers to send with the request
290
+ * @param {Boolean} [stripHeaders] - remove the default headers from the request
291
+ * @since 2.4.9
292
+ */
293
+ sendRequest = (
294
+ endpoint,
295
+ method = "GET",
296
+ body = undefined,
297
+ headers = {},
298
+ stripHeaders = false,
299
+ ) => {
300
+ let requestHeaders = this.defaultHeaders;
301
+ if (stripHeaders) {
302
+ headers = {};
303
+ }
304
+ requestHeaders = { ...requestHeaders, ...headers };
305
+ return fetch(`${this.apiRoot}${endpoint}`, {
306
+ headers: { Authorization: `Bearer ${this.userToken}`, ...requestHeaders },
307
+ body: body,
308
+ method: method,
309
+ });
310
+ };
311
+
312
+ /**
313
+ * Initiate the login process with a phone number. Should be followed up with verifySMSCode().
314
+ * @method
315
+ * @since 1.0.0
316
+ * @param {Number} phoneNumber - US phone number (WITHOUT +1) to send verification code to
317
+ */
318
+ loginViaSMS = async (phoneNumber) => {
319
+ try {
320
+ const res = await fetch(`${this.apiRoot}/v1/login_register`, {
321
+ method: "POST",
322
+ headers: this.defaultHeaders,
323
+ body: JSON.stringify({
324
+ phone_number: `+1${phoneNumber}`,
325
+ version: 3,
326
+ }),
327
+ });
328
+ const json = await res.json();
329
+ return json;
330
+ } catch (err) {
331
+ console.error(err);
332
+ throw new SidechatAPIError("Failed to request SMS verification.");
333
+ }
334
+ };
335
+
336
+ /**
337
+ * Verify the code sent via SMS with loginViaSMS(). If this function succeeds, the user will be authenticated for future requests.
338
+ * @method
339
+ * @since 1.0.0
340
+ * @param {Number} phoneNumber - US phone number (WITHOUT +1) that verification code was sent to
341
+ * @param {String} code - the verification code
342
+ */
343
+ verifySMSCode = async (phoneNumber, code) => {
344
+ try {
345
+ const res = await fetch(`${this.apiRoot}/v1/verify_phone_number`, {
346
+ method: "POST",
347
+ headers: this.defaultHeaders,
348
+ body: JSON.stringify({
349
+ phone_number: `+1${phoneNumber}`,
350
+ code: code.toUpperCase(),
351
+ }),
352
+ });
353
+ const json = await res.json();
354
+ if (json?.logged_in_user?.token) {
355
+ this.userToken = json.logged_in_user.token;
356
+ }
357
+ return json;
358
+ } catch (err) {
359
+ console.error(err);
360
+ throw new SidechatAPIError("Failed verify this code.");
361
+ }
362
+ };
363
+
364
+ /**
365
+ * Set the user's age. If this function succeeds, the user will be authenticated for future requests.
366
+ * @method
367
+ * @since 1.0.0
368
+ * @param {Number} age - user's age in years
369
+ * @param {String} registrationID - the registration ID generated by verifySMSCode()
370
+ */
371
+ setAge = async (age, registrationID) => {
372
+ if (age < 13) {
373
+ throw new SidechatAPIError("You're too young to use Offsides.");
374
+ }
375
+ try {
376
+ const res = await fetch(`${this.apiRoot}/v1/complete_registration`, {
377
+ method: "POST",
378
+ headers: this.defaultHeaders,
379
+ body: JSON.stringify({
380
+ age: Number(age),
381
+ registration_id: registrationID,
382
+ }),
383
+ });
384
+ const json = await res.json();
385
+ if (json.token) {
386
+ this.userToken = json.token;
387
+ }
388
+ return json;
389
+ } catch (err) {
390
+ console.error(err);
391
+ throw new SidechatAPIError("Failed verify this code.");
392
+ }
393
+ };
394
+
395
+ /**
396
+ * Initiate the email setup process. Should be followed up with checkEmailVerification().
397
+ * @method
398
+ * @since 1.0.0
399
+ * @param {String} email - school email address to send verification code to
400
+ * @tutorial Email Registration
401
+ */
402
+ registerEmail = async (email) => {
403
+ if (!this.userToken) {
404
+ throw new SidechatAPIError("User is not authenticated.");
405
+ }
406
+ try {
407
+ const res = await fetch(`${this.apiRoot}/v2/users/register_email`, {
408
+ method: "POST",
409
+ headers: {
410
+ ...this.defaultHeaders,
411
+ Authorization: `Bearer ${this.userToken}`,
412
+ },
413
+ body: JSON.stringify({
414
+ email: email,
415
+ }),
416
+ });
417
+ const json = await res.json();
418
+ if (json.message) {
419
+ throw new SidechatAPIError(json.message);
420
+ }
421
+ return json;
422
+ } catch (err) {
423
+ console.error(err);
424
+ throw new SidechatAPIError("Failed to request email verification.");
425
+ }
426
+ };
427
+
428
+ /**
429
+ * Check is the user's email is verified.
430
+ * @method
431
+ * @since 1.0.0
432
+ */
433
+ checkEmailVerification = async () => {
434
+ if (!this.userToken) {
435
+ throw new SidechatAPIError("User is not authenticated.");
436
+ }
437
+ try {
438
+ const res = await fetch(`${this.apiRoot}/v1/users/check_email_verified`, {
439
+ method: "GET",
440
+ headers: {
441
+ ...this.defaultHeaders,
442
+ Authorization: `Bearer ${this.userToken}`,
443
+ },
444
+ });
445
+ const json = await res.json();
446
+ if (json.verified_email_updates_response) {
447
+ return json.verified_email_updates_response;
448
+ } else if (json.changing_phone_number_verified_email_user) {
449
+ return json.changing_phone_number_verified_email_user;
450
+ } else {
451
+ throw new SidechatAPIError(json?.message || "Email is not verified.");
452
+ }
453
+ } catch (err) {
454
+ console.error(err);
455
+ throw new SidechatAPIError("Email is not verified.");
456
+ }
457
+ };
458
+
459
+ /**
460
+ * Set the device ID of the current user
461
+ * @method
462
+ * @since 1.0.0
463
+ * @param {String} deviceId - the device ID to set
464
+ */
465
+ setDeviceID = async (deviceID) => {
466
+ if (!this.userToken) {
467
+ throw new SidechatAPIError("User is not authenticated.");
468
+ }
469
+ try {
470
+ const res = await fetch(`${this.apiRoot}/v1/register_device_token`, {
471
+ method: "POST",
472
+ headers: {
473
+ ...this.defaultHeaders,
474
+ Authorization: `Bearer ${this.userToken}`,
475
+ },
476
+ body: JSON.stringify({
477
+ build_type: "release",
478
+ bundle_id: "com.flowerave.sidechat",
479
+ device_token: deviceID,
480
+ }),
481
+ });
482
+ const json = await res.json();
483
+ return json;
484
+ } catch (err) {
485
+ console.error(err);
486
+ throw new SidechatAPIError("Failed verify this code.");
487
+ }
488
+ };
489
+
490
+ /**
491
+ * Get updated status for user and group
492
+ * @method
493
+ * @since 1.0.0
494
+ * @deprecated since 2.1.0, will be removed by 3.0.0. Please use `getUpdates` instead!
495
+ * @param {String} [groupID] - ID of a specific group to retrieve info from. Falls back to user's primary group.
496
+ */
497
+ getUserAndGroup = async (groupID = "") => {
498
+ const json = await this.getUpdates(groupID);
499
+ return json;
500
+ };
501
+
502
+ /**
503
+ * Get updated status for user and group
504
+ * @method
505
+ * @since 2.1.0
506
+ * @param {String} [groupID] - ID of a specific group to retrieve info from. Falls back to user's primary group.
507
+ */
508
+ getUpdates = async (groupID = "") => {
509
+ if (!this.userToken) {
510
+ throw new SidechatAPIError("User is not authenticated.");
511
+ }
512
+ try {
513
+ const res = await fetch(
514
+ `${this.apiRoot}/v1/updates?group_id=${groupID}`,
515
+ {
516
+ method: "GET",
517
+ headers: {
518
+ ...this.defaultHeaders,
519
+ Authorization: `Bearer ${this.userToken}`,
520
+ },
521
+ },
522
+ );
523
+ const json = await res.json();
524
+ return json;
525
+ } catch (err) {
526
+ console.error(err);
527
+ throw new SidechatAPIError(`Failed to get posts from group.`);
528
+ }
529
+ };
530
+
531
+ /**
532
+ * Fetches posts from the specified category in a group
533
+ * @method
534
+ * @since 1.0.0
535
+ * @param {String} groupID - group ID
536
+ * @param {"hot"|"recent"|"top"} category - category to filter posts
537
+ * @param {types.SidechatCursorString} [cursor] - cursor string
538
+ * @returns {Promise<types.SidechatPostsAndCursor>} List of posts and cursor
539
+ */
540
+ getGroupPosts = async (groupID, category = "hot", cursor) => {
541
+ if (!this.userToken) {
542
+ throw new SidechatAPIError("User is not authenticated.");
543
+ }
544
+ try {
545
+ const res = await fetch(
546
+ `${this.apiRoot}/v1/posts?${
547
+ cursor ? "cursor=" + encodeURIComponent(cursor) + "&" : ""
548
+ }group_id=${groupID}&type=${category}&cacheBust=${Date.now()}`,
549
+ {
550
+ method: "GET",
551
+ headers: {
552
+ ...this.defaultHeaders,
553
+ Authorization: `Bearer ${this.userToken}`,
554
+ },
555
+ },
556
+ );
557
+ const json = await res.json();
558
+ return json;
559
+ } catch (err) {
560
+ console.error(err);
561
+ throw new SidechatAPIError(`Failed to get posts from group.`);
562
+ }
563
+ };
564
+
565
+ /**
566
+ * Upvote or downvote, or unvote a post
567
+ * @method
568
+ * @since 2.0.0-alpha.0
569
+ * @param {String} postID - post ID to vote on
570
+ * @param {types.SidechatVoteString} action - whether to upvote, downvote, or reset vote
571
+ */
572
+ setVote = async (postID, action) => {
573
+ if (!this.userToken) {
574
+ throw new SidechatAPIError("User is not authenticated.");
575
+ }
576
+ try {
577
+ const res = await fetch(`${this.apiRoot}/v1/posts/set_vote`, {
578
+ method: "POST",
579
+ headers: {
580
+ ...this.defaultHeaders,
581
+ Authorization: `Bearer ${this.userToken}`,
582
+ },
583
+ body: JSON.stringify({
584
+ post_id: postID,
585
+ vote_status: action,
586
+ }),
587
+ });
588
+ const json = await res.json();
589
+ return json;
590
+ } catch (err) {
591
+ console.error(err);
592
+ throw new SidechatAPIError(`Failed to change the vote on post.`);
593
+ }
594
+ };
595
+
596
+ /**
597
+ * Fetches a single post with just its ID
598
+ * @method
599
+ * @since 2.3.0
600
+ * @param {String} postID - ID of post to fetch
601
+ * @param {Boolean} includeDeleted - undocumented
602
+ * @returns {Promise<types.SidechatPostOrComment>} post object
603
+ */
604
+ getPost = async (postID, includeDeleted = false) => {
605
+ if (!this.userToken) {
606
+ throw new SidechatAPIError("User is not authenticated.");
607
+ }
608
+ try {
609
+ const res = await fetch(
610
+ `${this.apiRoot}/v1/posts/get?include_deleted=${includeDeleted}&post_id=${postID}&cacheBust=${Date.now()}`,
611
+ {
612
+ method: "GET",
613
+ headers: {
614
+ ...this.defaultHeaders,
615
+ Authorization: `Bearer ${this.userToken}`,
616
+ },
617
+ },
618
+ );
619
+ const json = await res.json();
620
+ return json.post;
621
+ } catch (err) {
622
+ console.error(err);
623
+ throw new SidechatAPIError(`Failed to get post from ID.`);
624
+ }
625
+ };
626
+
627
+ /**
628
+ * Fetches the posts or comments that the user has created
629
+ * @method
630
+ * @since 2.3.5
631
+ * @param {"posts"|"comments"} contentType - type of user content to fetch
632
+ * @returns {Promise<types.SidechatPostOrComment[]>} post object
633
+ */
634
+ getUserContent = async (contentType) => {
635
+ if (!this.userToken) {
636
+ throw new SidechatAPIError("User is not authenticated.");
637
+ }
638
+ if (contentType == "posts") {
639
+ contentType = "my_posts";
640
+ } else if (contentType == "comments") {
641
+ contentType = "my_comments";
642
+ }
643
+ try {
644
+ const res = await fetch(`${this.apiRoot}/v1/posts&type=${contentType}`, {
645
+ method: "GET",
646
+ headers: {
647
+ ...this.defaultHeaders,
648
+ Authorization: `Bearer ${this.userToken}`,
649
+ },
650
+ });
651
+ const json = await res.json();
652
+ return json.posts;
653
+ } catch (err) {
654
+ console.error(err);
655
+ throw new SidechatAPIError(`Failed to get content from user.`);
656
+ }
657
+ };
658
+
659
+ /**
660
+ * Get all the commments on a post
661
+ * @method
662
+ * @since 2.0.0-alpha.0
663
+ * @param {String} postID - post ID to get comments for
664
+ * @returns {Promise<types.SidechatPostOrComment[]>} list of comments
665
+ */
666
+ getPostComments = async (postID) => {
667
+ if (!this.userToken) {
668
+ throw new SidechatAPIError("User is not authenticated.");
669
+ }
670
+ try {
671
+ const res = await fetch(
672
+ `${this.apiRoot}/v1/posts/comments/?post_id=${postID}&cacheBust=${Date.now()}`,
673
+ {
674
+ method: "GET",
675
+ headers: {
676
+ ...this.defaultHeaders,
677
+ Authorization: `Bearer ${this.userToken}`,
678
+ },
679
+ },
680
+ );
681
+ const json = await res.json();
682
+ // Function to preprocess the comments and organize them into a nested structure
683
+ function preprocessComments(apiComments) {
684
+ // Map to store comments by their IDs for efficient lookup
685
+ const commentMap = new Map();
686
+ // List to store top-level comments
687
+ const topLevelComments = [];
688
+
689
+ // Iterate through the API comments
690
+ apiComments.forEach((comment) => {
691
+ // Store the comment in the map with its ID as the key
692
+ commentMap.set(comment.id, comment);
693
+ // Get the parent comment using the reply_post_id
694
+ const parentComment = commentMap.get(comment.reply_post_id);
695
+ // Check if the comment is a top-level comment
696
+ if (
697
+ !parentComment ||
698
+ comment.reply_post_id === comment.parent_post_id
699
+ ) {
700
+ // If it's a top-level comment, push it to the topLevelComments array
701
+ topLevelComments.push(comment);
702
+ } else {
703
+ // If it's a reply, add it to the parent comment's replies array
704
+ if (!parentComment.replies) parentComment.replies = [];
705
+ parentComment.replies.push(comment);
706
+ }
707
+ });
708
+
709
+ // Flatten the nested structure and return a single list of comments
710
+ return flattenComments(topLevelComments);
711
+ }
712
+
713
+ // Function to flatten nested comments into a single list
714
+ function flattenComments(comments) {
715
+ // Use reduce to flatten the nested comments array into a single list
716
+ return comments.reduce((flatComments, comment) => {
717
+ // Push the current comment to the flatComments array
718
+ flatComments.push(comment);
719
+ // If the current comment has replies, recursively flatten them and push to the flatComments array
720
+ if (comment.replies)
721
+ flatComments.push(...flattenComments(comment.replies));
722
+ // Return the flatComments array
723
+ return flatComments;
724
+ }, []);
725
+ }
726
+
727
+ const sortedComments = preprocessComments(json.posts);
728
+ return sortedComments;
729
+ } catch (err) {
730
+ console.error(err);
731
+ throw new SidechatAPIError(`Failed to get comments on post.`);
732
+ }
733
+ };
734
+
735
+ /**
736
+ * Gets groups to be displayed on the "Explore Groups" page
737
+ * @method
738
+ * @since 2.0.0-alpha.0
739
+ * @param {Boolean} onePage - whether or not results should be returned as a single page
740
+ * @returns {Promise<types.SidechatGroup[]>}
741
+ */
742
+ getAvailableGroups = async (onePage = true) => {
743
+ if (!this.userToken) {
744
+ throw new SidechatAPIError("User is not authenticated.");
745
+ }
746
+ try {
747
+ const res = await fetch(`${this.apiRoot}/v1/groups/explore`, {
748
+ method: "GET",
749
+ headers: {
750
+ ...this.defaultHeaders,
751
+ "App-Version": onePage ? "0" : this.defaultHeaders["App-Version"],
752
+ Authorization: `Bearer ${this.userToken}`,
753
+ },
754
+ });
755
+ const json = await res.json();
756
+ return await json.groups;
757
+ } catch (err) {
758
+ console.error(err);
759
+ throw new SidechatAPIError(`Failed to get groups from explore.`);
760
+ }
761
+ };
762
+
763
+ /**
764
+ * Searches for new groups based on a query keyword
765
+ * @method
766
+ * @since 2.6.0
767
+ * @param {String} query - the string to search for. This will be encoded, so strings with spaces and special characters are okay.
768
+ * @returns {Promise<types.SidechatGroup[]>}
769
+ */
770
+ searchAvailableGroups = async (query) => {
771
+ if (!this.userToken) {
772
+ throw new SidechatAPIError("User is not authenticated.");
773
+ }
774
+ try {
775
+ const res = await fetch(
776
+ `${this.apiRoot}/v1/groups/explore/search?term=${encodeURIComponent(query)}`,
777
+ {
778
+ method: "GET",
779
+ headers: {
780
+ ...this.defaultHeaders,
781
+ Authorization: `Bearer ${this.userToken}`,
782
+ },
783
+ },
784
+ );
785
+ const json = await res.json();
786
+ return await json.results;
787
+ } catch (err) {
788
+ console.error(err);
789
+ throw new SidechatAPIError(`Failed to search groups.`);
790
+ }
791
+ };
792
+
793
+ /**
794
+ * Retrieves the entire accessible asset library. Be warned that as of the time of this documentation, it's a 1.5MB JSON download and this request is very expensive.
795
+ * @method
796
+ * @since 2.0.6
797
+ * @returns {Promise<types.SidechatLibraryAsset[]>}
798
+ */
799
+ getAssetLibrary = async () => {
800
+ if (!this.userToken) {
801
+ throw new SidechatAPIError("User is not authenticated.");
802
+ }
803
+ try {
804
+ const res = await fetch(`${this.apiRoot}/v1/assets/library`, {
805
+ method: "GET",
806
+ headers: {
807
+ ...this.defaultHeaders,
808
+ Authorization: `Bearer ${this.userToken}`,
809
+ },
810
+ });
811
+ const json = await res.json();
812
+ return await json.items;
813
+ } catch (err) {
814
+ console.error(err);
815
+ throw new SidechatAPIError(`Failed to get asset library.`);
816
+ }
817
+ };
818
+
819
+ /**
820
+ * Gets the current authenticated user and a list of the groups of which they are members.
821
+ * @method
822
+ * @since 2.1.0
823
+ * @returns {Promise<types.SidechatCurrentUser>}
824
+ */
825
+ getCurrentUser = async () => {
826
+ if (!this.userToken) {
827
+ throw new SidechatAPIError("User is not authenticated.");
828
+ }
829
+ try {
830
+ const res = await fetch(`${this.apiRoot}/v1/users/me`, {
831
+ method: "GET",
832
+ headers: {
833
+ ...this.defaultHeaders,
834
+ Authorization: `Bearer ${this.userToken}`,
835
+ },
836
+ });
837
+ const json = await res.json();
838
+ return await json;
839
+ } catch (err) {
840
+ console.error(err);
841
+ throw new SidechatAPIError(`Failed to get asset library.`);
842
+ }
843
+ };
844
+
845
+ /**
846
+ * Gets the metadata of a group from its ID
847
+ * @method
848
+ * @since 2.1.0
849
+ * @param {String} [groupID] - alphanumeric ID of a group to get. Falls back to user's primary group.
850
+ * @returns {Promise<types.SidechatGroup>}
851
+ */
852
+ getGroupMetadata = async (groupID = "") => {
853
+ if (!this.userToken) {
854
+ throw new SidechatAPIError("User is not authenticated.");
855
+ }
856
+ try {
857
+ const res = await fetch(`${this.apiRoot}/v1/groups/${groupID}`, {
858
+ method: "GET",
859
+ headers: {
860
+ ...this.defaultHeaders,
861
+ Authorization: `Bearer ${this.userToken}`,
862
+ },
863
+ });
864
+ const json = await res.json();
865
+ return await json.group;
866
+ } catch (err) {
867
+ console.error(err);
868
+ throw new SidechatAPIError(`Failed to get group metadata.`);
869
+ }
870
+ };
871
+
872
+ /**
873
+ * Joins or leaves a group
874
+ * @method
875
+ * @param {String} groupID - alphanumeric ID of group to join or leave
876
+ * @param {Boolean} isMember - whether or not the user should be a member of the group
877
+ * @since 2.3.8
878
+ */
879
+ setGroupMembership = async (groupID, isMember) => {
880
+ if (!this.userToken) {
881
+ throw new SidechatAPIError("User is not authenticated.");
882
+ }
883
+ try {
884
+ const res = await fetch(
885
+ `${this.apiRoot}/v1/groups/${isMember ? "join" : "leave"}`,
886
+ {
887
+ method: "POST",
888
+ headers: {
889
+ ...this.defaultHeaders,
890
+ Authorization: `Bearer ${this.userToken}`,
891
+ },
892
+ body: JSON.stringify({
893
+ group_id: groupID,
894
+ }),
895
+ },
896
+ );
897
+ const json = await res.json();
898
+ return await json;
899
+ } catch (err) {
900
+ console.error(err);
901
+ throw new SidechatAPIError(`Failed to modify group membership.`);
902
+ }
903
+ };
904
+
905
+ /**
906
+ * Creates a comment on a post
907
+ * @method
908
+ * @since 2.2.0
909
+ * @param {String} parentPostID - alphanumeric ID of post on which this comment is made
910
+ * @param {String} text - text content of comment
911
+ * @param {String} groupID - alphanumeric ID of group in which the parent post resides
912
+ * @param {String} [replyCommentID] - alphanumeric ID of comment to reply to. Falls back to parentPostID
913
+ * @param {String} [topLevelReplyID] - alphanumeric ID of the top-level comment to reply to. Used only when replying to replies. Falls back to parentPostID
914
+ * @param {types.SidechatSimpleAsset[]} [assetList] - list of assets to attach
915
+ * @param {Boolean} [disableDMs] - prevent direct messages being sent to comment's author
916
+ * @param {Boolean} [anonymous] - whether or not to hide user's name and icon on comment
917
+ * @returns {Promise<types.SidechatPostOrComment>} created comment
918
+ */
919
+ createComment = async (
920
+ parentPostID,
921
+ text,
922
+ groupID,
923
+ replyCommentID,
924
+ topLevelReplyID,
925
+ assetList = [],
926
+ disableDMs = false,
927
+ anonymous = false,
928
+ ) => {
929
+ if (!this.userToken) {
930
+ throw new SidechatAPIError("User is not authenticated.");
931
+ }
932
+ try {
933
+ const res = await fetch(`${this.apiRoot}/v1/posts`, {
934
+ method: "POST",
935
+ headers: {
936
+ ...this.defaultHeaders,
937
+ Authorization: `Bearer ${this.userToken}`,
938
+ },
939
+ body: JSON.stringify({
940
+ type: "comment",
941
+ assets: assetList,
942
+ group_ids: [groupID],
943
+ text: text,
944
+ reply_post_id: topLevelReplyID || replyCommentID || parentPostID,
945
+ reply_comment_post_id: replyCommentID || parentPostID,
946
+ parent_post_id: parentPostID,
947
+ dms_disabled: disableDMs,
948
+ using_identity: !anonymous,
949
+ }),
950
+ });
951
+ const json = await res.json();
952
+ return await json.comment;
953
+ } catch (err) {
954
+ console.error(err);
955
+ throw new SidechatAPIError(`Failed to post comment.`);
956
+ }
957
+ };
958
+
959
+ /**
960
+ * Creates a new post in the specified group
961
+ * @method
962
+ * @since 2.2.0
963
+ * @param {String} text - text content of comment
964
+ * @param {String} groupID - alphanumeric ID of group in which the parent post resides
965
+ * @param {types.SidechatSimpleAsset[]} [assetList] - list of assets to attach.
966
+ * @param {Boolean} [disableDMs] - prevent direct messages from being sent to post's author
967
+ * @param {Boolean} [disableComments] - whether or not comments should be disabled on post
968
+ * @param {Boolean} [anonymous] - whether or not to hide user's name and icon on post
969
+ * @param {Array<String>} [pollOptions] - List of poll options. If provided, a poll will be created with these options.
970
+ * @returns {Promise<types.SidechatPostOrComment>} the created post
971
+ */
972
+ createPost = async (
973
+ text,
974
+ groupID,
975
+ assetList = [],
976
+ disableDMs = false,
977
+ disableComments = false,
978
+ anonymous = false,
979
+ repostId = undefined,
980
+ pollOptions = undefined,
981
+ ) => {
982
+ if (!this.userToken) {
983
+ throw new SidechatAPIError("User is not authenticated.");
984
+ }
985
+ try {
986
+ // Build the request body
987
+ const body = {
988
+ type: "post",
989
+ assets: assetList,
990
+ group_ids: [groupID],
991
+ text: text,
992
+ attachments: [],
993
+ dms_disabled: disableDMs,
994
+ comments_disabled: disableComments,
995
+ using_identity: !anonymous,
996
+ quote_post_id: repostId,
997
+ };
998
+ // If pollOptions is provided and is a non-empty array, add poll_request
999
+ if (Array.isArray(pollOptions) && pollOptions.length > 0) {
1000
+ body.poll_request = {
1001
+ allows_view_results: true,
1002
+ choices: pollOptions,
1003
+ };
1004
+ }
1005
+ const res = await fetch(`${this.apiRoot}/v1/posts`, {
1006
+ method: "POST",
1007
+ headers: {
1008
+ ...this.defaultHeaders,
1009
+ Authorization: `Bearer ${this.userToken}`,
1010
+ },
1011
+ body: JSON.stringify(body),
1012
+ });
1013
+ const json = await res.json();
1014
+ return await json.posts[0];
1015
+ } catch (err) {
1016
+ console.error(err);
1017
+ throw new SidechatAPIError(`Failed to make post.`);
1018
+ }
1019
+ };
1020
+
1021
+ /**
1022
+ * Deletes a post or comment that the user created
1023
+ * @method
1024
+ * @since 2.2.0
1025
+ * @param {String} postOrCommentID - alphanumeric ID of post to delete
1026
+ */
1027
+ deletePostOrComment = async (postOrCommentID) => {
1028
+ if (!this.userToken) {
1029
+ throw new SidechatAPIError("User is not authenticated.");
1030
+ }
1031
+ try {
1032
+ const res = await fetch(`${this.apiRoot}/v1/posts/delete`, {
1033
+ method: "POST",
1034
+ headers: {
1035
+ ...this.defaultHeaders,
1036
+ Authorization: `Bearer ${this.userToken}`,
1037
+ },
1038
+ body: JSON.stringify({
1039
+ post_id: postOrCommentID,
1040
+ }),
1041
+ });
1042
+ const json = await res.json();
1043
+ return await json;
1044
+ } catch (err) {
1045
+ console.error(err);
1046
+ throw new SidechatAPIError(`Failed to delete post.`);
1047
+ }
1048
+ };
1049
+
1050
+ /**
1051
+ * Votes on a poll attached to a post
1052
+ * @method
1053
+ * @param {String} pollId - alphanumeric ID of poll to vote on
1054
+ * @param {Number} choiceIndex - index of the choice to vote on
1055
+ * @since 2.5.4
1056
+ */
1057
+ voteOnPoll = async (pollId, choiceIndex) => {
1058
+ if (!this.userToken) {
1059
+ throw new SidechatAPIError("User is not authenticated.");
1060
+ }
1061
+ try {
1062
+ const res = await fetch(`${this.apiRoot}/v1/polls/vote`, {
1063
+ method: "POST",
1064
+ headers: {
1065
+ ...this.defaultHeaders,
1066
+ Authorization: `Bearer ${this.userToken}`,
1067
+ },
1068
+ body: JSON.stringify({
1069
+ poll_id: pollId,
1070
+ choice: choiceIndex,
1071
+ }),
1072
+ });
1073
+ const json = await res.json();
1074
+ return await json;
1075
+ } catch (err) {
1076
+ console.error(err);
1077
+ throw new SidechatAPIError(`Failed to vote on poll`);
1078
+ }
1079
+ };
1080
+
1081
+ /**
1082
+ * Marks that the user has viewed results on a poll. Note that this does not actually return the results of the poll.
1083
+ * @method
1084
+ * @param {String} pollId - alphanumeric ID of poll to vote on
1085
+ * @since 2.5.4
1086
+ */
1087
+ viewPollResults = async (pollId) => {
1088
+ if (!this.userToken) {
1089
+ throw new SidechatAPIError("User is not authenticated.");
1090
+ }
1091
+ try {
1092
+ const res = await fetch(
1093
+ `${this.apiRoot}/v1/polls/view_results&cacheBust=${Date.now()}`,
1094
+ {
1095
+ method: "POST",
1096
+ headers: {
1097
+ ...this.defaultHeaders,
1098
+ Authorization: `Bearer ${this.userToken}`,
1099
+ },
1100
+ body: JSON.stringify({
1101
+ poll_id: pollId,
1102
+ }),
1103
+ },
1104
+ );
1105
+ const json = await res.json();
1106
+ return await json;
1107
+ } catch (err) {
1108
+ console.error(err);
1109
+ throw new SidechatAPIError(`Failed to mark poll results as viewed.`);
1110
+ }
1111
+ };
1112
+
1113
+ /**
1114
+ * Uploads an asset to AWS S3 for use in posts and comments. Currently photos only
1115
+ * @method
1116
+ * @param {String} uri - URI of the asset to upload
1117
+ * @param {String} mimeType - mimetype of the asset (e.g. "image/png")
1118
+ * @param {String} [name] - filename of the asset
1119
+ * @returns {Promise<String>} URL of the uploaded asset
1120
+ * @since 2.5.1
1121
+ */
1122
+ uploadAsset = async (uri, mimeType, name = "") => {
1123
+ if (!this.userToken) {
1124
+ throw new SidechatAPIError("User is not authenticated.");
1125
+ }
1126
+
1127
+ let imageType = mimeType.split("/")[1];
1128
+ if (!["png", "jpeg", "gif"].includes(imageType)) {
1129
+ throw new SidechatAPIError("Unsupported image format.");
1130
+ }
1131
+
1132
+ const data = new FormData();
1133
+ data.append("image", {
1134
+ name: name,
1135
+ type: mimeType,
1136
+ uri: uri,
1137
+ });
1138
+
1139
+ const urlReq = await this.sendRequest(
1140
+ `/v1/assets/upload_url?content_type=${imageType}`,
1141
+ );
1142
+ const urlJson = await urlReq.json();
1143
+
1144
+ try {
1145
+ const uploadReq = await fetch(urlJson.upload_url, {
1146
+ body: data.getAll("image")[0],
1147
+ method: "PUT",
1148
+ headers: {
1149
+ "Content-Type": mimeType,
1150
+ },
1151
+ });
1152
+ if (uploadReq.status == 200) {
1153
+ return `${this.apiRoot}/v1/assets/library/${urlJson.asset_id}`;
1154
+ } else {
1155
+ throw new SidechatAPIError(
1156
+ `Couldn't upload image - error ${uploadReq.status}`,
1157
+ );
1158
+ }
1159
+ } catch (e) {
1160
+ throw new SidechatAPIError(e.message);
1161
+ }
1162
+ };
1163
+
1164
+ /**
1165
+ * Sets the conversation icon of a user
1166
+ * @method
1167
+ * @since 2.2.1
1168
+ * @param {String} userID - alphanumeric ID of user
1169
+ * @param {String} emoji - emoji to set as icon
1170
+ * @param {String} primaryColor - hex string (including #) of primary color
1171
+ * @param {String} secondaryColor - hex string (including #) of secondary color
1172
+ */
1173
+ setUserIcon = async (userID, emoji, primaryColor, secondaryColor) => {
1174
+ if (!this.userToken) {
1175
+ throw new SidechatAPIError("User is not authenticated.");
1176
+ }
1177
+ try {
1178
+ const res = await fetch(`${this.apiRoot}/v1/users/${userID}`, {
1179
+ method: "PATCH",
1180
+ headers: {
1181
+ ...this.defaultHeaders,
1182
+ "App-Version": "0",
1183
+ Authorization: `Bearer ${this.userToken}`,
1184
+ },
1185
+ body: JSON.stringify({
1186
+ conversation_icon: {
1187
+ emoji: emoji,
1188
+ secondary_color: secondaryColor,
1189
+ is_migrated: true,
1190
+ color: primaryColor,
1191
+ },
1192
+ }),
1193
+ });
1194
+ const json = await res.json();
1195
+ return await json;
1196
+ } catch (err) {
1197
+ console.error(err);
1198
+ throw new SidechatAPIError(`Failed to set icon.`);
1199
+ }
1200
+ };
1201
+
1202
+ /**
1203
+ * Sets the bio text of a user
1204
+ * @method
1205
+ * @since 2.5.6
1206
+ * @param {String} userID - alphanumeric ID of user
1207
+ * @param {String} bio - text to set as bio
1208
+ */
1209
+ setUserBio = async (userID, bio) => {
1210
+ if (!this.userToken) {
1211
+ throw new SidechatAPIError("User is not authenticated.");
1212
+ }
1213
+ try {
1214
+ const res = await fetch(`${this.apiRoot}/v1/users/${userID}`, {
1215
+ method: "PATCH",
1216
+ headers: {
1217
+ ...this.defaultHeaders,
1218
+ "App-Version": "5.4.22",
1219
+ Authorization: `Bearer ${this.userToken}`,
1220
+ },
1221
+ body: JSON.stringify({
1222
+ bio: bio,
1223
+ }),
1224
+ });
1225
+ const json = await res.json();
1226
+ return await json;
1227
+ } catch (err) {
1228
+ console.error(err);
1229
+ throw new SidechatAPIError(`Failed to set bio.`);
1230
+ }
1231
+ };
1232
+
1233
+ /**
1234
+ * Checks if user can set their username to a string
1235
+ * @method
1236
+ * @since 2.3.6
1237
+ * @param {String} username - string to check
1238
+ * @returns {Promise<Boolean>} whether or not username is valid and unused
1239
+ */
1240
+ checkUsername = async (username) => {
1241
+ if (!this.userToken) {
1242
+ throw new SidechatAPIError("User is not authenticated.");
1243
+ }
1244
+ try {
1245
+ const res = await fetch(
1246
+ `${this.apiRoot}/v1/users/username?username=${username}`,
1247
+ {
1248
+ method: "GET",
1249
+ headers: {
1250
+ ...this.defaultHeaders,
1251
+ Authorization: `Bearer ${this.userToken}`,
1252
+ },
1253
+ },
1254
+ );
1255
+ if (res?.status == 200 || res?.status == 204) {
1256
+ return true;
1257
+ } else {
1258
+ return false;
1259
+ }
1260
+ } catch (err) {
1261
+ console.error(err);
1262
+ throw new SidechatAPIError(`Failed to check username.`);
1263
+ }
1264
+ };
1265
+
1266
+ /**
1267
+ * Changes the username of the current user
1268
+ * @method
1269
+ * @since 2.3.6
1270
+ * @param {String} userID - alphanumeric ID of user
1271
+ * @param {String} username - new username to set
1272
+ */
1273
+ setUsername = async (userID, username) => {
1274
+ if (!this.userToken) {
1275
+ throw new SidechatAPIError("User is not authenticated.");
1276
+ }
1277
+ try {
1278
+ const res = await fetch(`${this.apiRoot}/v1/users/${userID}`, {
1279
+ method: "PATCH",
1280
+ headers: {
1281
+ ...this.defaultHeaders,
1282
+ Authorization: `Bearer ${this.userToken}`,
1283
+ },
1284
+ body: JSON.stringify({
1285
+ username: username,
1286
+ }),
1287
+ });
1288
+ const json = await res.json();
1289
+ return await json.user;
1290
+ } catch (err) {
1291
+ console.error(err);
1292
+ throw new SidechatAPIError(`Failed to set icon.`);
1293
+ }
1294
+ };
1295
+
1296
+ /**
1297
+ * Fetches a public user profile
1298
+ * @method
1299
+ * @since 2.6.0
1300
+ * @param {String} username - username of the user to fetch
1301
+ * @returns {Promise<types.SidechatProfile>}
1302
+ */
1303
+ getUserProfile = async (username) => {
1304
+ if (!this.userToken) {
1305
+ throw new SidechatAPIError("User is not authenticated.");
1306
+ }
1307
+ try {
1308
+ const res = await fetch(
1309
+ `${this.apiRoot}/v1/groups/username?username=${username}&cacheBust=${Date.now()}`,
1310
+ {
1311
+ method: "GET",
1312
+ headers: {
1313
+ ...this.defaultHeaders,
1314
+ Authorization: `Bearer ${this.userToken}`,
1315
+ },
1316
+ },
1317
+ );
1318
+ const json = await res.json();
1319
+ return await json.group;
1320
+ } catch (err) {
1321
+ console.error(err);
1322
+ throw new SidechatAPIError(`Failed to set icon.`);
1323
+ }
1324
+ };
1325
+
1326
+ /**
1327
+ * Fetches a public user's posts
1328
+ * @method
1329
+ * @since 2.6.0
1330
+ * @param {String} username - username of the user to fetch
1331
+ * @returns {Promise<types.SidechatPostOrComment[]>}
1332
+ */
1333
+ getUserPosts = async (username) => {
1334
+ if (!this.userToken) {
1335
+ throw new SidechatAPIError("User is not authenticated.");
1336
+ }
1337
+ try {
1338
+ const res = await fetch(
1339
+ `${this.apiRoot}/v1/users/posts?username=${username}`,
1340
+ {
1341
+ method: "GET",
1342
+ headers: {
1343
+ ...this.defaultHeaders,
1344
+ Authorization: `Bearer ${this.userToken}`,
1345
+ },
1346
+ },
1347
+ );
1348
+ const json = await res.json();
1349
+ return await json.posts;
1350
+ } catch (err) {
1351
+ console.error(err);
1352
+ throw new SidechatAPIError(`Failed to set icon.`);
1353
+ }
1354
+ };
1355
+
1356
+ /**
1357
+ * Marks an activity item as read
1358
+ * @method
1359
+ * @since 2.3.2
1360
+ * @param {String} activityID - alphanumeric ID of activity object
1361
+ */
1362
+ readActivity = async (activityID) => {
1363
+ if (!this.userToken) {
1364
+ throw new SidechatAPIError("User is not authenticated.");
1365
+ }
1366
+ try {
1367
+ const res = await fetch(`${this.apiRoot}/v1/activity/seen`, {
1368
+ method: "POST",
1369
+ headers: {
1370
+ ...this.defaultHeaders,
1371
+ Authorization: `Bearer ${this.userToken}`,
1372
+ },
1373
+ body: JSON.stringify({
1374
+ ids: [activityID],
1375
+ }),
1376
+ });
1377
+ const json = await res.json();
1378
+ return await json;
1379
+ } catch (err) {
1380
+ console.error(err);
1381
+ throw new SidechatAPIError(`Failed to mark activity as read.`);
1382
+ }
1383
+ };
1384
+
1385
+ /**
1386
+ * Retrieves joinable group chats
1387
+ * @method
1388
+ * @since 2.3.5
1389
+ */
1390
+ getGroupChats = async () => {
1391
+ if (!this.userToken) {
1392
+ throw new SidechatAPIError("User is not authenticated.");
1393
+ }
1394
+ try {
1395
+ const res = await fetch(
1396
+ `${this.apiRoot}/v1/chats/explore&cacheBust=${Date.now()}`,
1397
+ {
1398
+ method: "GET",
1399
+ headers: {
1400
+ ...this.defaultHeaders,
1401
+ Authorization: `Bearer ${this.userToken}`,
1402
+ },
1403
+ },
1404
+ );
1405
+ const json = await res.json();
1406
+ return await json.chats;
1407
+ } catch (err) {
1408
+ console.error(err);
1409
+ throw new SidechatAPIError(`Failed to get groupchats.`);
1410
+ }
1411
+ };
1412
+
1413
+ /**
1414
+ * Joins a group chat. To mimic the official client, use the user's display name and icon by default.
1415
+ * @method
1416
+ * @param {String} groupChatID - alphanumeric ID of group chat to join
1417
+ * @param {String} displayName - display name to use in chat
1418
+ * @param {String} emoji - emoji to use as icon
1419
+ * @param {String} primaryColor - hex string of primary color
1420
+ * @param {String} secondaryColor - hex string of secondary color
1421
+ * @since 2.3.5
1422
+ */
1423
+ joinGroupChat = async (
1424
+ groupChatID,
1425
+ displayName,
1426
+ emoji,
1427
+ primaryColor,
1428
+ secondaryColor,
1429
+ ) => {
1430
+ if (!this.userToken) {
1431
+ throw new SidechatAPIError("User is not authenticated.");
1432
+ }
1433
+ try {
1434
+ const res = await fetch(`${this.apiRoot}/v1/chats/groups/join`, {
1435
+ method: "POST",
1436
+ headers: {
1437
+ ...this.defaultHeaders,
1438
+ Authorization: `Bearer ${this.userToken}`,
1439
+ },
1440
+ body: JSON.stringify({
1441
+ chat_id: groupChatID,
1442
+ identity: {
1443
+ display_name: displayName,
1444
+ emoji: emoji,
1445
+ secondary_color: secondaryColor,
1446
+ color: primaryColor,
1447
+ },
1448
+ }),
1449
+ });
1450
+ const json = await res.json();
1451
+ return await json;
1452
+ } catch (err) {
1453
+ console.error(err);
1454
+ throw new SidechatAPIError(`Failed to join groupchat.`);
1455
+ }
1456
+ };
1457
+
1458
+ /**
1459
+ * Gets a list of the user's direct messages
1460
+ * @method
1461
+ * @returns {Promise<types.SidechatDirectThread[]>}
1462
+ * @since 2.4.4
1463
+ */
1464
+ getDMs = async () => {
1465
+ if (!this.userToken) {
1466
+ throw new SidechatAPIError("User is not authenticated.");
1467
+ }
1468
+ try {
1469
+ const res = await fetch(`${this.apiRoot}/v1/chats`, {
1470
+ method: "GET",
1471
+ headers: {
1472
+ ...this.defaultHeaders,
1473
+ Authorization: `Bearer ${this.userToken}`,
1474
+ },
1475
+ });
1476
+ const json = await res.json();
1477
+ const list = await json.chats;
1478
+ let r = [];
1479
+ list.forEach((o) => {
1480
+ r.push(o.chat);
1481
+ });
1482
+ return await r;
1483
+ } catch (err) {
1484
+ console.error(err);
1485
+ throw new SidechatAPIError(`Failed to fetch DMs.`);
1486
+ }
1487
+ };
1488
+
1489
+ /**
1490
+ * Gets a single direct message thread
1491
+ * @method
1492
+ * @param {String} id - alphanumeric ID of the chat to fetch
1493
+ * @returns {Promise<types.SidechatDirectThread>}
1494
+ * @since 2.4.4
1495
+ */
1496
+ getDMThread = async (id) => {
1497
+ if (!this.userToken) {
1498
+ throw new SidechatAPIError("User is not authenticated.");
1499
+ }
1500
+ try {
1501
+ const res = await fetch(
1502
+ `${this.apiRoot}/v1/chats/messages?chat_id=${id}`,
1503
+ {
1504
+ method: "GET",
1505
+ headers: {
1506
+ ...this.defaultHeaders,
1507
+ Authorization: `Bearer ${this.userToken}`,
1508
+ },
1509
+ },
1510
+ );
1511
+ const json = await res.json();
1512
+ return await json.chat;
1513
+ } catch (err) {
1514
+ console.error(err);
1515
+ throw new SidechatAPIError(`Failed to fetch DM thread.`);
1516
+ }
1517
+ };
1518
+
1519
+ /**
1520
+ * Sends a message to an existing direct message thread - note that you must first use startDM() to start a thread.
1521
+ * @method
1522
+ * @param {String} chatID - alphanumeric ID of the chat to send to
1523
+ * @param {String} text - text content of message
1524
+ * @param {String} clientID - alphanumeric device ID
1525
+ * @param {types.SidechatAsset[]} assets - array of assets to send
1526
+ * @param {Boolean} anonymous - whether the DM should be sent anonymously
1527
+ * @since 2.4.4
1528
+ */
1529
+ sendDM = async (chatID, text, clientID, assets = [], anonymous = false) => {
1530
+ if (!this.userToken) {
1531
+ throw new SidechatAPIError("User is not authenticated.");
1532
+ }
1533
+ try {
1534
+ const res = await fetch(`${this.apiRoot}/v1/chats/send`, {
1535
+ method: "POST",
1536
+ headers: {
1537
+ ...this.defaultHeaders,
1538
+ Authorization: `Bearer ${this.userToken}`,
1539
+ },
1540
+ body: JSON.stringify({
1541
+ chat_id: chatID,
1542
+ text: text,
1543
+ client_id: clientID,
1544
+ anonymous: anonymous,
1545
+ assets: assets,
1546
+ }),
1547
+ });
1548
+ const json = await res.json();
1549
+ return await json;
1550
+ } catch (err) {
1551
+ console.error(err);
1552
+ throw new SidechatAPIError(`Failed to send message.`);
1553
+ }
1554
+ };
1555
+
1556
+ /**
1557
+ * Creates a new direct message thread
1558
+ * @method
1559
+ * @param {String} text - text content of message
1560
+ * @param {String} clientID - alphanumeric ID of devide
1561
+ * @param {String} postID - alphanumeric ID of post or comment
1562
+ * @param {Boolean} anonymous - whether the DM should be sent anonymously
1563
+ * @param {"feed"} postContext - context of post (mostly undocumented, defaults to "feed")
1564
+ * @since 2.4.4
1565
+ */
1566
+ startDM = async (
1567
+ text,
1568
+ clientID,
1569
+ postID,
1570
+ anonymous = false,
1571
+ postContext = "feed",
1572
+ ) => {
1573
+ if (!this.userToken) {
1574
+ throw new SidechatAPIError("User is not authenticated.");
1575
+ }
1576
+ try {
1577
+ const res = await fetch(`${this.apiRoot}/v1/chats/start`, {
1578
+ method: "POST",
1579
+ headers: {
1580
+ ...this.defaultHeaders,
1581
+ Authorization: `Bearer ${this.userToken}`,
1582
+ },
1583
+ body: JSON.stringify({
1584
+ text: text,
1585
+ client_id: clientID,
1586
+ post_id: postID,
1587
+ anonymous: anonymous,
1588
+ post_context: postContext,
1589
+ }),
1590
+ });
1591
+ const json = await res.json();
1592
+ return await json;
1593
+ } catch (err) {
1594
+ console.error(err);
1595
+ throw new SidechatAPIError(`Failed to start DM.`);
1596
+ }
1597
+ };
1598
+
1599
+ /**
1600
+ * Hides posts from user
1601
+ * @method
1602
+ * @param {String} postID - alphanumeric ID of post to hide
1603
+ * @since 2.6.2
1604
+ */
1605
+ hidePostsFromUser = async (postID) => {
1606
+ if (!this.userToken) {
1607
+ throw new SidechatAPIError("User is not authenticated.");
1608
+ }
1609
+ try {
1610
+ const res = await fetch(`${this.apiRoot}/v1/posts/hide_posts_from_user`, {
1611
+ method: "POST",
1612
+ headers: {
1613
+ ...this.defaultHeaders,
1614
+ Authorization: `Bearer ${this.userToken}`,
1615
+ },
1616
+ body: JSON.stringify({
1617
+ post_id: postID,
1618
+ post_context: "feed",
1619
+ report: false,
1620
+ }),
1621
+ });
1622
+ const json = await res.json();
1623
+ return await json;
1624
+ } catch (err) {
1625
+ console.error(err);
1626
+ throw new SidechatAPIError(`Failed to hide post.`);
1627
+ }
1628
+ };
1629
+
1630
+ /**
1631
+ * Unhides all posts from all users
1632
+ * @method
1633
+ * @since 2.6.2
1634
+ */
1635
+ unhidePostsFromAllUsers = async () => {
1636
+ if (!this.userToken) {
1637
+ throw new SidechatAPIError("User is not authenticated.");
1638
+ }
1639
+ try {
1640
+ const res = await fetch(`${this.apiRoot}/v1/users/unhide_all`, {
1641
+ method: "POST",
1642
+ headers: {
1643
+ ...this.defaultHeaders,
1644
+ Authorization: `Bearer ${this.userToken}`,
1645
+ },
1646
+ body: JSON.stringify({}),
1647
+ });
1648
+ const json = await res.json();
1649
+ return await json;
1650
+ } catch (err) {
1651
+ console.error(err);
1652
+ throw new SidechatAPIError(`Failed to unhide post.`);
1653
+ }
1654
+ };
1624
1655
  }
1625
1656
 
1626
1657
  /**