spectrum-ts 4.2.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -67
- package/dist/authoring.d.ts +1 -6
- package/dist/authoring.js +2 -36
- package/dist/elysia.d.ts +1 -94
- package/dist/elysia.js +2 -15
- package/dist/express.d.ts +1 -62
- package/dist/express.js +2 -19
- package/dist/hono.d.ts +1 -64
- package/dist/hono.js +2 -11
- package/dist/index.d.ts +1 -2851
- package/dist/index.js +2 -3763
- package/dist/manifest.json +5 -5
- package/dist/providers/imessage/index.d.ts +1 -222
- package/dist/providers/imessage/index.js +2 -25
- package/dist/providers/index.d.ts +6 -19
- package/dist/providers/index.js +6 -34
- package/dist/providers/slack/index.d.ts +1 -46
- package/dist/providers/slack/index.js +2 -11
- package/dist/providers/telegram/index.d.ts +1 -45
- package/dist/providers/telegram/index.js +2 -13
- package/dist/providers/terminal/index.d.ts +1 -119
- package/dist/providers/terminal/index.js +2 -13
- package/dist/providers/whatsapp-business/index.d.ts +1 -27
- package/dist/providers/whatsapp-business/index.js +2 -14
- package/package.json +11 -38
- package/dist/attachment-CnivEhr6.d.ts +0 -29
- package/dist/authoring-b9AhXgPI.d.ts +0 -304
- package/dist/chunk-2D27WW5B.js +0 -63
- package/dist/chunk-34FQGGD7.js +0 -34
- package/dist/chunk-3GEJYGZK.js +0 -84
- package/dist/chunk-5XEFJBN2.js +0 -197
- package/dist/chunk-6UZFVXQF.js +0 -374
- package/dist/chunk-A37PM5N2.js +0 -91
- package/dist/chunk-ARL2NOBO.js +0 -887
- package/dist/chunk-B52VPQO3.js +0 -1379
- package/dist/chunk-DMPDLSFU.js +0 -864
- package/dist/chunk-FAIFTUV2.js +0 -139
- package/dist/chunk-LZXPLXZF.js +0 -35
- package/dist/chunk-N6THJDZV.js +0 -929
- package/dist/chunk-NLMQ75LH.js +0 -2980
- package/dist/chunk-UXAKIXVM.js +0 -409
- package/dist/chunk-WXLQNANA.js +0 -539
- package/dist/chunk-ZR3TKZMT.js +0 -129
- package/dist/read-C4uvozGX.d.ts +0 -53
- package/dist/types-CyfLJXgu.d.ts +0 -1530
- package/dist/types-ZgFTj5hJ.d.ts +0 -87
package/dist/index.js
CHANGED
|
@@ -1,3763 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
FUSOR_MESSAGES_CHANNEL,
|
|
4
|
-
fusor,
|
|
5
|
-
fusorEvent,
|
|
6
|
-
isFusorClient,
|
|
7
|
-
isFusorEvent
|
|
8
|
-
} from "./chunk-34FQGGD7.js";
|
|
9
|
-
import {
|
|
10
|
-
voice
|
|
11
|
-
} from "./chunk-FAIFTUV2.js";
|
|
12
|
-
import {
|
|
13
|
-
asRichlink,
|
|
14
|
-
richlink
|
|
15
|
-
} from "./chunk-ZR3TKZMT.js";
|
|
16
|
-
import {
|
|
17
|
-
group
|
|
18
|
-
} from "./chunk-LZXPLXZF.js";
|
|
19
|
-
import {
|
|
20
|
-
option,
|
|
21
|
-
poll
|
|
22
|
-
} from "./chunk-2D27WW5B.js";
|
|
23
|
-
import {
|
|
24
|
-
asContact,
|
|
25
|
-
contact
|
|
26
|
-
} from "./chunk-A37PM5N2.js";
|
|
27
|
-
import {
|
|
28
|
-
fromVCard,
|
|
29
|
-
toVCard
|
|
30
|
-
} from "./chunk-6UZFVXQF.js";
|
|
31
|
-
import {
|
|
32
|
-
SpectrumCloudError,
|
|
33
|
-
cloud
|
|
34
|
-
} from "./chunk-3GEJYGZK.js";
|
|
35
|
-
import {
|
|
36
|
-
broadcast,
|
|
37
|
-
createAsyncQueue,
|
|
38
|
-
mergeStreams,
|
|
39
|
-
stream
|
|
40
|
-
} from "./chunk-5XEFJBN2.js";
|
|
41
|
-
import {
|
|
42
|
-
UnsupportedError,
|
|
43
|
-
avatar,
|
|
44
|
-
buildSpace,
|
|
45
|
-
contentAttrs,
|
|
46
|
-
definePlatform,
|
|
47
|
-
edit,
|
|
48
|
-
markdown,
|
|
49
|
-
read,
|
|
50
|
-
rename,
|
|
51
|
-
reply,
|
|
52
|
-
senderAttrs,
|
|
53
|
-
typing,
|
|
54
|
-
unsend,
|
|
55
|
-
wrapProviderMessage
|
|
56
|
-
} from "./chunk-B52VPQO3.js";
|
|
57
|
-
import {
|
|
58
|
-
asAttachment,
|
|
59
|
-
asCustom,
|
|
60
|
-
attachment,
|
|
61
|
-
custom,
|
|
62
|
-
reaction,
|
|
63
|
-
resolveContents,
|
|
64
|
-
text
|
|
65
|
-
} from "./chunk-UXAKIXVM.js";
|
|
66
|
-
|
|
67
|
-
// src/emoji/generated.ts
|
|
68
|
-
var GeneratedEmoji = {
|
|
69
|
-
_1stPlaceMedal: "\u{1F947}",
|
|
70
|
-
_2ndPlaceMedal: "\u{1F948}",
|
|
71
|
-
_3rdPlaceMedal: "\u{1F949}",
|
|
72
|
-
abacus: "\u{1F9EE}",
|
|
73
|
-
abButton: "\u{1F18E}",
|
|
74
|
-
aButton: "\u{1F170}\uFE0F",
|
|
75
|
-
accordion: "\u{1FA97}",
|
|
76
|
-
adhesiveBandage: "\u{1FA79}",
|
|
77
|
-
admissionTickets: "\u{1F39F}\uFE0F",
|
|
78
|
-
aerialTramway: "\u{1F6A1}",
|
|
79
|
-
airplane: "\u2708\uFE0F",
|
|
80
|
-
airplaneArrival: "\u{1F6EC}",
|
|
81
|
-
airplaneDeparture: "\u{1F6EB}",
|
|
82
|
-
alarmClock: "\u23F0",
|
|
83
|
-
alembic: "\u2697\uFE0F",
|
|
84
|
-
alien: "\u{1F47D}",
|
|
85
|
-
alienMonster: "\u{1F47E}",
|
|
86
|
-
ambulance: "\u{1F691}",
|
|
87
|
-
americanFootball: "\u{1F3C8}",
|
|
88
|
-
amphora: "\u{1F3FA}",
|
|
89
|
-
anatomicalHeart: "\u{1FAC0}",
|
|
90
|
-
anchor: "\u2693",
|
|
91
|
-
angerSymbol: "\u{1F4A2}",
|
|
92
|
-
angryFace: "\u{1F620}",
|
|
93
|
-
angryFaceWithHorns: "\u{1F47F}",
|
|
94
|
-
anguishedFace: "\u{1F627}",
|
|
95
|
-
ant: "\u{1F41C}",
|
|
96
|
-
antennaBars: "\u{1F4F6}",
|
|
97
|
-
anxiousFaceWithSweat: "\u{1F630}",
|
|
98
|
-
aquarius: "\u2652",
|
|
99
|
-
aries: "\u2648",
|
|
100
|
-
articulatedLorry: "\u{1F69B}",
|
|
101
|
-
artist: "\u{1F9D1}\u200D\u{1F3A8}",
|
|
102
|
-
artistPalette: "\u{1F3A8}",
|
|
103
|
-
astonishedFace: "\u{1F632}",
|
|
104
|
-
astronaut: "\u{1F9D1}\u200D\u{1F680}",
|
|
105
|
-
atmSign: "\u{1F3E7}",
|
|
106
|
-
atomSymbol: "\u269B\uFE0F",
|
|
107
|
-
automobile: "\u{1F697}",
|
|
108
|
-
autoRickshaw: "\u{1F6FA}",
|
|
109
|
-
avocado: "\u{1F951}",
|
|
110
|
-
axe: "\u{1FA93}",
|
|
111
|
-
baby: "\u{1F476}",
|
|
112
|
-
babyAngel: "\u{1F47C}",
|
|
113
|
-
babyBottle: "\u{1F37C}",
|
|
114
|
-
babyChick: "\u{1F424}",
|
|
115
|
-
babySymbol: "\u{1F6BC}",
|
|
116
|
-
backArrow: "\u{1F519}",
|
|
117
|
-
backhandIndexPointingDown: "\u{1F447}",
|
|
118
|
-
backhandIndexPointingLeft: "\u{1F448}",
|
|
119
|
-
backhandIndexPointingRight: "\u{1F449}",
|
|
120
|
-
backhandIndexPointingUp: "\u{1F446}",
|
|
121
|
-
backpack: "\u{1F392}",
|
|
122
|
-
bacon: "\u{1F953}",
|
|
123
|
-
badger: "\u{1F9A1}",
|
|
124
|
-
badminton: "\u{1F3F8}",
|
|
125
|
-
bagel: "\u{1F96F}",
|
|
126
|
-
baggageClaim: "\u{1F6C4}",
|
|
127
|
-
baguetteBread: "\u{1F956}",
|
|
128
|
-
balanceScale: "\u2696\uFE0F",
|
|
129
|
-
balletDancer: "\u{1F9D1}\u200D\u{1FA70}",
|
|
130
|
-
balletShoes: "\u{1FA70}",
|
|
131
|
-
balloon: "\u{1F388}",
|
|
132
|
-
ballotBoxWithBallot: "\u{1F5F3}\uFE0F",
|
|
133
|
-
banana: "\u{1F34C}",
|
|
134
|
-
banjo: "\u{1FA95}",
|
|
135
|
-
bank: "\u{1F3E6}",
|
|
136
|
-
barberPole: "\u{1F488}",
|
|
137
|
-
barChart: "\u{1F4CA}",
|
|
138
|
-
baseball: "\u26BE",
|
|
139
|
-
basket: "\u{1F9FA}",
|
|
140
|
-
basketball: "\u{1F3C0}",
|
|
141
|
-
bat: "\u{1F987}",
|
|
142
|
-
bathtub: "\u{1F6C1}",
|
|
143
|
-
battery: "\u{1F50B}",
|
|
144
|
-
bButton: "\u{1F171}\uFE0F",
|
|
145
|
-
beachWithUmbrella: "\u{1F3D6}\uFE0F",
|
|
146
|
-
beamingFaceWithSmilingEyes: "\u{1F601}",
|
|
147
|
-
beans: "\u{1FAD8}",
|
|
148
|
-
bear: "\u{1F43B}",
|
|
149
|
-
beatingHeart: "\u{1F493}",
|
|
150
|
-
beaver: "\u{1F9AB}",
|
|
151
|
-
bed: "\u{1F6CF}\uFE0F",
|
|
152
|
-
beerMug: "\u{1F37A}",
|
|
153
|
-
beetle: "\u{1FAB2}",
|
|
154
|
-
bell: "\u{1F514}",
|
|
155
|
-
bellhopBell: "\u{1F6CE}\uFE0F",
|
|
156
|
-
bellPepper: "\u{1FAD1}",
|
|
157
|
-
bellWithSlash: "\u{1F515}",
|
|
158
|
-
bentoBox: "\u{1F371}",
|
|
159
|
-
beverageBox: "\u{1F9C3}",
|
|
160
|
-
bicycle: "\u{1F6B2}",
|
|
161
|
-
bikini: "\u{1F459}",
|
|
162
|
-
billedCap: "\u{1F9E2}",
|
|
163
|
-
biohazard: "\u2623\uFE0F",
|
|
164
|
-
bird: "\u{1F426}",
|
|
165
|
-
birthdayCake: "\u{1F382}",
|
|
166
|
-
bison: "\u{1F9AC}",
|
|
167
|
-
bitingLip: "\u{1FAE6}",
|
|
168
|
-
blackBird: "\u{1F426}\u200D\u2B1B",
|
|
169
|
-
blackCat: "\u{1F408}\u200D\u2B1B",
|
|
170
|
-
blackCircle: "\u26AB",
|
|
171
|
-
blackFlag: "\u{1F3F4}",
|
|
172
|
-
blackHeart: "\u{1F5A4}",
|
|
173
|
-
blackLargeSquare: "\u2B1B",
|
|
174
|
-
blackMediumSmallSquare: "\u25FE",
|
|
175
|
-
blackMediumSquare: "\u25FC\uFE0F",
|
|
176
|
-
blackNib: "\u2712\uFE0F",
|
|
177
|
-
blackSmallSquare: "\u25AA\uFE0F",
|
|
178
|
-
blackSquareButton: "\u{1F532}",
|
|
179
|
-
blossom: "\u{1F33C}",
|
|
180
|
-
blowfish: "\u{1F421}",
|
|
181
|
-
blueberries: "\u{1FAD0}",
|
|
182
|
-
blueBook: "\u{1F4D8}",
|
|
183
|
-
blueCircle: "\u{1F535}",
|
|
184
|
-
blueHeart: "\u{1F499}",
|
|
185
|
-
blueSquare: "\u{1F7E6}",
|
|
186
|
-
boar: "\u{1F417}",
|
|
187
|
-
bomb: "\u{1F4A3}",
|
|
188
|
-
bone: "\u{1F9B4}",
|
|
189
|
-
bookmark: "\u{1F516}",
|
|
190
|
-
bookmarkTabs: "\u{1F4D1}",
|
|
191
|
-
books: "\u{1F4DA}",
|
|
192
|
-
boomerang: "\u{1FA83}",
|
|
193
|
-
bottleWithPoppingCork: "\u{1F37E}",
|
|
194
|
-
bouquet: "\u{1F490}",
|
|
195
|
-
bowAndArrow: "\u{1F3F9}",
|
|
196
|
-
bowling: "\u{1F3B3}",
|
|
197
|
-
bowlWithSpoon: "\u{1F963}",
|
|
198
|
-
boxingGlove: "\u{1F94A}",
|
|
199
|
-
boy: "\u{1F466}",
|
|
200
|
-
brain: "\u{1F9E0}",
|
|
201
|
-
bread: "\u{1F35E}",
|
|
202
|
-
breastFeeding: "\u{1F931}",
|
|
203
|
-
brick: "\u{1F9F1}",
|
|
204
|
-
bridgeAtNight: "\u{1F309}",
|
|
205
|
-
briefcase: "\u{1F4BC}",
|
|
206
|
-
briefs: "\u{1FA72}",
|
|
207
|
-
brightButton: "\u{1F506}",
|
|
208
|
-
broccoli: "\u{1F966}",
|
|
209
|
-
brokenChain: "\u26D3\uFE0F\u200D\u{1F4A5}",
|
|
210
|
-
brokenHeart: "\u{1F494}",
|
|
211
|
-
broom: "\u{1F9F9}",
|
|
212
|
-
brownCircle: "\u{1F7E4}",
|
|
213
|
-
brownHeart: "\u{1F90E}",
|
|
214
|
-
brownMushroom: "\u{1F344}\u200D\u{1F7EB}",
|
|
215
|
-
brownSquare: "\u{1F7EB}",
|
|
216
|
-
bubbles: "\u{1FAE7}",
|
|
217
|
-
bubbleTea: "\u{1F9CB}",
|
|
218
|
-
bucket: "\u{1FAA3}",
|
|
219
|
-
bug: "\u{1F41B}",
|
|
220
|
-
buildingConstruction: "\u{1F3D7}\uFE0F",
|
|
221
|
-
bulletTrain: "\u{1F685}",
|
|
222
|
-
bullseye: "\u{1F3AF}",
|
|
223
|
-
burrito: "\u{1F32F}",
|
|
224
|
-
bus: "\u{1F68C}",
|
|
225
|
-
busStop: "\u{1F68F}",
|
|
226
|
-
bustInSilhouette: "\u{1F464}",
|
|
227
|
-
bustsInSilhouette: "\u{1F465}",
|
|
228
|
-
butter: "\u{1F9C8}",
|
|
229
|
-
butterfly: "\u{1F98B}",
|
|
230
|
-
cactus: "\u{1F335}",
|
|
231
|
-
calendar: "\u{1F4C5}",
|
|
232
|
-
callMeHand: "\u{1F919}",
|
|
233
|
-
camel: "\u{1F42A}",
|
|
234
|
-
camera: "\u{1F4F7}",
|
|
235
|
-
cameraWithFlash: "\u{1F4F8}",
|
|
236
|
-
camping: "\u{1F3D5}\uFE0F",
|
|
237
|
-
cancer: "\u264B",
|
|
238
|
-
candle: "\u{1F56F}\uFE0F",
|
|
239
|
-
candy: "\u{1F36C}",
|
|
240
|
-
cannedFood: "\u{1F96B}",
|
|
241
|
-
canoe: "\u{1F6F6}",
|
|
242
|
-
capricorn: "\u2651",
|
|
243
|
-
cardFileBox: "\u{1F5C3}\uFE0F",
|
|
244
|
-
cardIndex: "\u{1F4C7}",
|
|
245
|
-
cardIndexDividers: "\u{1F5C2}\uFE0F",
|
|
246
|
-
carouselHorse: "\u{1F3A0}",
|
|
247
|
-
carpentrySaw: "\u{1FA9A}",
|
|
248
|
-
carpStreamer: "\u{1F38F}",
|
|
249
|
-
carrot: "\u{1F955}",
|
|
250
|
-
castle: "\u{1F3F0}",
|
|
251
|
-
cat: "\u{1F408}",
|
|
252
|
-
catFace: "\u{1F431}",
|
|
253
|
-
catWithTearsOfJoy: "\u{1F639}",
|
|
254
|
-
catWithWrySmile: "\u{1F63C}",
|
|
255
|
-
chains: "\u26D3\uFE0F",
|
|
256
|
-
chair: "\u{1FA91}",
|
|
257
|
-
chartDecreasing: "\u{1F4C9}",
|
|
258
|
-
chartIncreasing: "\u{1F4C8}",
|
|
259
|
-
chartIncreasingWithYen: "\u{1F4B9}",
|
|
260
|
-
checkBoxWithCheck: "\u2611\uFE0F",
|
|
261
|
-
checkMark: "\u2714\uFE0F",
|
|
262
|
-
checkMarkButton: "\u2705",
|
|
263
|
-
cheeseWedge: "\u{1F9C0}",
|
|
264
|
-
chequeredFlag: "\u{1F3C1}",
|
|
265
|
-
cherries: "\u{1F352}",
|
|
266
|
-
cherryBlossom: "\u{1F338}",
|
|
267
|
-
chessPawn: "\u265F\uFE0F",
|
|
268
|
-
chestnut: "\u{1F330}",
|
|
269
|
-
chicken: "\u{1F414}",
|
|
270
|
-
child: "\u{1F9D2}",
|
|
271
|
-
childrenCrossing: "\u{1F6B8}",
|
|
272
|
-
chipmunk: "\u{1F43F}\uFE0F",
|
|
273
|
-
chocolateBar: "\u{1F36B}",
|
|
274
|
-
chopsticks: "\u{1F962}",
|
|
275
|
-
christmasTree: "\u{1F384}",
|
|
276
|
-
church: "\u26EA",
|
|
277
|
-
cigarette: "\u{1F6AC}",
|
|
278
|
-
cinema: "\u{1F3A6}",
|
|
279
|
-
circledM: "\u24C2\uFE0F",
|
|
280
|
-
circusTent: "\u{1F3AA}",
|
|
281
|
-
cityscape: "\u{1F3D9}\uFE0F",
|
|
282
|
-
cityscapeAtDusk: "\u{1F306}",
|
|
283
|
-
clamp: "\u{1F5DC}\uFE0F",
|
|
284
|
-
clapperBoard: "\u{1F3AC}",
|
|
285
|
-
clappingHands: "\u{1F44F}",
|
|
286
|
-
classicalBuilding: "\u{1F3DB}\uFE0F",
|
|
287
|
-
clButton: "\u{1F191}",
|
|
288
|
-
clinkingBeerMugs: "\u{1F37B}",
|
|
289
|
-
clinkingGlasses: "\u{1F942}",
|
|
290
|
-
clipboard: "\u{1F4CB}",
|
|
291
|
-
clockwiseVerticalArrows: "\u{1F503}",
|
|
292
|
-
closedBook: "\u{1F4D5}",
|
|
293
|
-
closedMailboxWithLoweredFlag: "\u{1F4EA}",
|
|
294
|
-
closedMailboxWithRaisedFlag: "\u{1F4EB}",
|
|
295
|
-
closedUmbrella: "\u{1F302}",
|
|
296
|
-
cloud: "\u2601\uFE0F",
|
|
297
|
-
cloudWithLightning: "\u{1F329}\uFE0F",
|
|
298
|
-
cloudWithLightningAndRain: "\u26C8\uFE0F",
|
|
299
|
-
cloudWithRain: "\u{1F327}\uFE0F",
|
|
300
|
-
cloudWithSnow: "\u{1F328}\uFE0F",
|
|
301
|
-
clownFace: "\u{1F921}",
|
|
302
|
-
clubSuit: "\u2663\uFE0F",
|
|
303
|
-
clutchBag: "\u{1F45D}",
|
|
304
|
-
coat: "\u{1F9E5}",
|
|
305
|
-
cockroach: "\u{1FAB3}",
|
|
306
|
-
cocktailGlass: "\u{1F378}",
|
|
307
|
-
coconut: "\u{1F965}",
|
|
308
|
-
coffin: "\u26B0\uFE0F",
|
|
309
|
-
coin: "\u{1FA99}",
|
|
310
|
-
coldFace: "\u{1F976}",
|
|
311
|
-
collision: "\u{1F4A5}",
|
|
312
|
-
comet: "\u2604\uFE0F",
|
|
313
|
-
compass: "\u{1F9ED}",
|
|
314
|
-
computerDisk: "\u{1F4BD}",
|
|
315
|
-
computerMouse: "\u{1F5B1}\uFE0F",
|
|
316
|
-
confettiBall: "\u{1F38A}",
|
|
317
|
-
confoundedFace: "\u{1F616}",
|
|
318
|
-
confusedFace: "\u{1F615}",
|
|
319
|
-
construction: "\u{1F6A7}",
|
|
320
|
-
constructionWorker: "\u{1F477}",
|
|
321
|
-
controlKnobs: "\u{1F39B}\uFE0F",
|
|
322
|
-
convenienceStore: "\u{1F3EA}",
|
|
323
|
-
cook: "\u{1F9D1}\u200D\u{1F373}",
|
|
324
|
-
cookedRice: "\u{1F35A}",
|
|
325
|
-
cookie: "\u{1F36A}",
|
|
326
|
-
cooking: "\u{1F373}",
|
|
327
|
-
coolButton: "\u{1F192}",
|
|
328
|
-
copyright: "\xA9\uFE0F",
|
|
329
|
-
coral: "\u{1FAB8}",
|
|
330
|
-
couchAndLamp: "\u{1F6CB}\uFE0F",
|
|
331
|
-
counterclockwiseArrowsButton: "\u{1F504}",
|
|
332
|
-
coupleWithHeart: "\u{1F491}",
|
|
333
|
-
coupleWithHeartManMan: "\u{1F468}\u200D\u2764\uFE0F\u200D\u{1F468}",
|
|
334
|
-
coupleWithHeartWomanMan: "\u{1F469}\u200D\u2764\uFE0F\u200D\u{1F468}",
|
|
335
|
-
coupleWithHeartWomanWoman: "\u{1F469}\u200D\u2764\uFE0F\u200D\u{1F469}",
|
|
336
|
-
cow: "\u{1F404}",
|
|
337
|
-
cowboyHatFace: "\u{1F920}",
|
|
338
|
-
cowFace: "\u{1F42E}",
|
|
339
|
-
crab: "\u{1F980}",
|
|
340
|
-
crayon: "\u{1F58D}\uFE0F",
|
|
341
|
-
creditCard: "\u{1F4B3}",
|
|
342
|
-
crescentMoon: "\u{1F319}",
|
|
343
|
-
cricket: "\u{1F997}",
|
|
344
|
-
cricketGame: "\u{1F3CF}",
|
|
345
|
-
crocodile: "\u{1F40A}",
|
|
346
|
-
croissant: "\u{1F950}",
|
|
347
|
-
crossedFingers: "\u{1F91E}",
|
|
348
|
-
crossedFlags: "\u{1F38C}",
|
|
349
|
-
crossedSwords: "\u2694\uFE0F",
|
|
350
|
-
crossMark: "\u274C",
|
|
351
|
-
crossMarkButton: "\u274E",
|
|
352
|
-
crown: "\u{1F451}",
|
|
353
|
-
crutch: "\u{1FA7C}",
|
|
354
|
-
cryingCat: "\u{1F63F}",
|
|
355
|
-
cryingFace: "\u{1F622}",
|
|
356
|
-
crystalBall: "\u{1F52E}",
|
|
357
|
-
cucumber: "\u{1F952}",
|
|
358
|
-
cupcake: "\u{1F9C1}",
|
|
359
|
-
cupWithStraw: "\u{1F964}",
|
|
360
|
-
curlingStone: "\u{1F94C}",
|
|
361
|
-
curlyLoop: "\u27B0",
|
|
362
|
-
currencyExchange: "\u{1F4B1}",
|
|
363
|
-
curryRice: "\u{1F35B}",
|
|
364
|
-
custard: "\u{1F36E}",
|
|
365
|
-
customs: "\u{1F6C3}",
|
|
366
|
-
cutOfMeat: "\u{1F969}",
|
|
367
|
-
cyclone: "\u{1F300}",
|
|
368
|
-
dagger: "\u{1F5E1}\uFE0F",
|
|
369
|
-
dango: "\u{1F361}",
|
|
370
|
-
dashingAway: "\u{1F4A8}",
|
|
371
|
-
deafMan: "\u{1F9CF}\u200D\u2642\uFE0F",
|
|
372
|
-
deafPerson: "\u{1F9CF}",
|
|
373
|
-
deafWoman: "\u{1F9CF}\u200D\u2640\uFE0F",
|
|
374
|
-
deciduousTree: "\u{1F333}",
|
|
375
|
-
deer: "\u{1F98C}",
|
|
376
|
-
deliveryTruck: "\u{1F69A}",
|
|
377
|
-
departmentStore: "\u{1F3EC}",
|
|
378
|
-
derelictHouse: "\u{1F3DA}\uFE0F",
|
|
379
|
-
desert: "\u{1F3DC}\uFE0F",
|
|
380
|
-
desertIsland: "\u{1F3DD}\uFE0F",
|
|
381
|
-
desktopComputer: "\u{1F5A5}\uFE0F",
|
|
382
|
-
detective: "\u{1F575}\uFE0F",
|
|
383
|
-
diamondSuit: "\u2666\uFE0F",
|
|
384
|
-
diamondWithADot: "\u{1F4A0}",
|
|
385
|
-
dimButton: "\u{1F505}",
|
|
386
|
-
disappointedFace: "\u{1F61E}",
|
|
387
|
-
disguisedFace: "\u{1F978}",
|
|
388
|
-
distortedFace: "\u{1FAEA}",
|
|
389
|
-
divide: "\u2797",
|
|
390
|
-
divingMask: "\u{1F93F}",
|
|
391
|
-
diyaLamp: "\u{1FA94}",
|
|
392
|
-
dizzy: "\u{1F4AB}",
|
|
393
|
-
dna: "\u{1F9EC}",
|
|
394
|
-
dodo: "\u{1F9A4}",
|
|
395
|
-
dog: "\u{1F415}",
|
|
396
|
-
dogFace: "\u{1F436}",
|
|
397
|
-
dollarBanknote: "\u{1F4B5}",
|
|
398
|
-
dolphin: "\u{1F42C}",
|
|
399
|
-
donkey: "\u{1FACF}",
|
|
400
|
-
door: "\u{1F6AA}",
|
|
401
|
-
dottedLineFace: "\u{1FAE5}",
|
|
402
|
-
dottedSixPointedStar: "\u{1F52F}",
|
|
403
|
-
doubleCurlyLoop: "\u27BF",
|
|
404
|
-
doubleExclamationMark: "\u203C\uFE0F",
|
|
405
|
-
doughnut: "\u{1F369}",
|
|
406
|
-
dove: "\u{1F54A}\uFE0F",
|
|
407
|
-
downArrow: "\u2B07\uFE0F",
|
|
408
|
-
downcastFaceWithSweat: "\u{1F613}",
|
|
409
|
-
downLeftArrow: "\u2199\uFE0F",
|
|
410
|
-
downRightArrow: "\u2198\uFE0F",
|
|
411
|
-
downwardsButton: "\u{1F53D}",
|
|
412
|
-
dragon: "\u{1F409}",
|
|
413
|
-
dragonFace: "\u{1F432}",
|
|
414
|
-
dress: "\u{1F457}",
|
|
415
|
-
droolingFace: "\u{1F924}",
|
|
416
|
-
droplet: "\u{1F4A7}",
|
|
417
|
-
dropOfBlood: "\u{1FA78}",
|
|
418
|
-
drum: "\u{1F941}",
|
|
419
|
-
duck: "\u{1F986}",
|
|
420
|
-
dumpling: "\u{1F95F}",
|
|
421
|
-
dvd: "\u{1F4C0}",
|
|
422
|
-
eagle: "\u{1F985}",
|
|
423
|
-
ear: "\u{1F442}",
|
|
424
|
-
earOfCorn: "\u{1F33D}",
|
|
425
|
-
earWithHearingAid: "\u{1F9BB}",
|
|
426
|
-
egg: "\u{1F95A}",
|
|
427
|
-
eggplant: "\u{1F346}",
|
|
428
|
-
eightOClock: "\u{1F557}",
|
|
429
|
-
eightPointedStar: "\u2734\uFE0F",
|
|
430
|
-
eightSpokedAsterisk: "\u2733\uFE0F",
|
|
431
|
-
eightThirty: "\u{1F563}",
|
|
432
|
-
ejectButton: "\u23CF\uFE0F",
|
|
433
|
-
electricPlug: "\u{1F50C}",
|
|
434
|
-
elephant: "\u{1F418}",
|
|
435
|
-
elevator: "\u{1F6D7}",
|
|
436
|
-
elevenOClock: "\u{1F55A}",
|
|
437
|
-
elevenThirty: "\u{1F566}",
|
|
438
|
-
elf: "\u{1F9DD}",
|
|
439
|
-
eMail: "\u{1F4E7}",
|
|
440
|
-
emptyNest: "\u{1FAB9}",
|
|
441
|
-
endArrow: "\u{1F51A}",
|
|
442
|
-
enragedFace: "\u{1F621}",
|
|
443
|
-
envelope: "\u2709\uFE0F",
|
|
444
|
-
envelopeWithArrow: "\u{1F4E9}",
|
|
445
|
-
euroBanknote: "\u{1F4B6}",
|
|
446
|
-
evergreenTree: "\u{1F332}",
|
|
447
|
-
ewe: "\u{1F411}",
|
|
448
|
-
exclamationQuestionMark: "\u2049\uFE0F",
|
|
449
|
-
explodingHead: "\u{1F92F}",
|
|
450
|
-
expressionlessFace: "\u{1F611}",
|
|
451
|
-
eye: "\u{1F441}\uFE0F",
|
|
452
|
-
eyeInSpeechBubble: "\u{1F441}\uFE0F\u200D\u{1F5E8}\uFE0F",
|
|
453
|
-
eyes: "\u{1F440}",
|
|
454
|
-
faceBlowingAKiss: "\u{1F618}",
|
|
455
|
-
faceExhaling: "\u{1F62E}\u200D\u{1F4A8}",
|
|
456
|
-
faceHoldingBackTears: "\u{1F979}",
|
|
457
|
-
faceInClouds: "\u{1F636}\u200D\u{1F32B}\uFE0F",
|
|
458
|
-
faceSavoringFood: "\u{1F60B}",
|
|
459
|
-
faceScreamingInFear: "\u{1F631}",
|
|
460
|
-
faceVomiting: "\u{1F92E}",
|
|
461
|
-
faceWithBagsUnderEyes: "\u{1FAE9}",
|
|
462
|
-
faceWithCrossedOutEyes: "\u{1F635}",
|
|
463
|
-
faceWithDiagonalMouth: "\u{1FAE4}",
|
|
464
|
-
faceWithHandOverMouth: "\u{1F92D}",
|
|
465
|
-
faceWithHeadBandage: "\u{1F915}",
|
|
466
|
-
faceWithMedicalMask: "\u{1F637}",
|
|
467
|
-
faceWithMonocle: "\u{1F9D0}",
|
|
468
|
-
faceWithOpenEyesAndHandOverMouth: "\u{1FAE2}",
|
|
469
|
-
faceWithOpenMouth: "\u{1F62E}",
|
|
470
|
-
faceWithoutMouth: "\u{1F636}",
|
|
471
|
-
faceWithPeekingEye: "\u{1FAE3}",
|
|
472
|
-
faceWithRaisedEyebrow: "\u{1F928}",
|
|
473
|
-
faceWithRollingEyes: "\u{1F644}",
|
|
474
|
-
faceWithSpiralEyes: "\u{1F635}\u200D\u{1F4AB}",
|
|
475
|
-
faceWithSteamFromNose: "\u{1F624}",
|
|
476
|
-
faceWithSymbolsOnMouth: "\u{1F92C}",
|
|
477
|
-
faceWithTearsOfJoy: "\u{1F602}",
|
|
478
|
-
faceWithThermometer: "\u{1F912}",
|
|
479
|
-
faceWithTongue: "\u{1F61B}",
|
|
480
|
-
factory: "\u{1F3ED}",
|
|
481
|
-
factoryWorker: "\u{1F9D1}\u200D\u{1F3ED}",
|
|
482
|
-
fairy: "\u{1F9DA}",
|
|
483
|
-
falafel: "\u{1F9C6}",
|
|
484
|
-
fallenLeaf: "\u{1F342}",
|
|
485
|
-
family: "\u{1F46A}",
|
|
486
|
-
familyAdultAdultChild: "\u{1F9D1}\u200D\u{1F9D1}\u200D\u{1F9D2}",
|
|
487
|
-
familyAdultAdultChildChild: "\u{1F9D1}\u200D\u{1F9D1}\u200D\u{1F9D2}\u200D\u{1F9D2}",
|
|
488
|
-
familyAdultChild: "\u{1F9D1}\u200D\u{1F9D2}",
|
|
489
|
-
familyAdultChildChild: "\u{1F9D1}\u200D\u{1F9D2}\u200D\u{1F9D2}",
|
|
490
|
-
familyManBoy: "\u{1F468}\u200D\u{1F466}",
|
|
491
|
-
familyManBoyBoy: "\u{1F468}\u200D\u{1F466}\u200D\u{1F466}",
|
|
492
|
-
familyManGirl: "\u{1F468}\u200D\u{1F467}",
|
|
493
|
-
familyManGirlBoy: "\u{1F468}\u200D\u{1F467}\u200D\u{1F466}",
|
|
494
|
-
familyManGirlGirl: "\u{1F468}\u200D\u{1F467}\u200D\u{1F467}",
|
|
495
|
-
familyManManBoy: "\u{1F468}\u200D\u{1F468}\u200D\u{1F466}",
|
|
496
|
-
familyManManBoyBoy: "\u{1F468}\u200D\u{1F468}\u200D\u{1F466}\u200D\u{1F466}",
|
|
497
|
-
familyManManGirl: "\u{1F468}\u200D\u{1F468}\u200D\u{1F467}",
|
|
498
|
-
familyManManGirlBoy: "\u{1F468}\u200D\u{1F468}\u200D\u{1F467}\u200D\u{1F466}",
|
|
499
|
-
familyManManGirlGirl: "\u{1F468}\u200D\u{1F468}\u200D\u{1F467}\u200D\u{1F467}",
|
|
500
|
-
familyManWomanBoy: "\u{1F468}\u200D\u{1F469}\u200D\u{1F466}",
|
|
501
|
-
familyManWomanBoyBoy: "\u{1F468}\u200D\u{1F469}\u200D\u{1F466}\u200D\u{1F466}",
|
|
502
|
-
familyManWomanGirl: "\u{1F468}\u200D\u{1F469}\u200D\u{1F467}",
|
|
503
|
-
familyManWomanGirlBoy: "\u{1F468}\u200D\u{1F469}\u200D\u{1F467}\u200D\u{1F466}",
|
|
504
|
-
familyManWomanGirlGirl: "\u{1F468}\u200D\u{1F469}\u200D\u{1F467}\u200D\u{1F467}",
|
|
505
|
-
familyWomanBoy: "\u{1F469}\u200D\u{1F466}",
|
|
506
|
-
familyWomanBoyBoy: "\u{1F469}\u200D\u{1F466}\u200D\u{1F466}",
|
|
507
|
-
familyWomanGirl: "\u{1F469}\u200D\u{1F467}",
|
|
508
|
-
familyWomanGirlBoy: "\u{1F469}\u200D\u{1F467}\u200D\u{1F466}",
|
|
509
|
-
familyWomanGirlGirl: "\u{1F469}\u200D\u{1F467}\u200D\u{1F467}",
|
|
510
|
-
familyWomanWomanBoy: "\u{1F469}\u200D\u{1F469}\u200D\u{1F466}",
|
|
511
|
-
familyWomanWomanBoyBoy: "\u{1F469}\u200D\u{1F469}\u200D\u{1F466}\u200D\u{1F466}",
|
|
512
|
-
familyWomanWomanGirl: "\u{1F469}\u200D\u{1F469}\u200D\u{1F467}",
|
|
513
|
-
familyWomanWomanGirlBoy: "\u{1F469}\u200D\u{1F469}\u200D\u{1F467}\u200D\u{1F466}",
|
|
514
|
-
familyWomanWomanGirlGirl: "\u{1F469}\u200D\u{1F469}\u200D\u{1F467}\u200D\u{1F467}",
|
|
515
|
-
farmer: "\u{1F9D1}\u200D\u{1F33E}",
|
|
516
|
-
fastDownButton: "\u23EC",
|
|
517
|
-
fastForwardButton: "\u23E9",
|
|
518
|
-
fastReverseButton: "\u23EA",
|
|
519
|
-
fastUpButton: "\u23EB",
|
|
520
|
-
faxMachine: "\u{1F4E0}",
|
|
521
|
-
fearfulFace: "\u{1F628}",
|
|
522
|
-
feather: "\u{1FAB6}",
|
|
523
|
-
femaleSign: "\u2640\uFE0F",
|
|
524
|
-
ferrisWheel: "\u{1F3A1}",
|
|
525
|
-
ferry: "\u26F4\uFE0F",
|
|
526
|
-
fieldHockey: "\u{1F3D1}",
|
|
527
|
-
fightCloud: "\u{1FAEF}",
|
|
528
|
-
fileCabinet: "\u{1F5C4}\uFE0F",
|
|
529
|
-
fileFolder: "\u{1F4C1}",
|
|
530
|
-
filmFrames: "\u{1F39E}\uFE0F",
|
|
531
|
-
filmProjector: "\u{1F4FD}\uFE0F",
|
|
532
|
-
fingerprint: "\u{1FAC6}",
|
|
533
|
-
fire: "\u{1F525}",
|
|
534
|
-
firecracker: "\u{1F9E8}",
|
|
535
|
-
fireEngine: "\u{1F692}",
|
|
536
|
-
fireExtinguisher: "\u{1F9EF}",
|
|
537
|
-
firefighter: "\u{1F9D1}\u200D\u{1F692}",
|
|
538
|
-
fireworks: "\u{1F386}",
|
|
539
|
-
firstQuarterMoon: "\u{1F313}",
|
|
540
|
-
firstQuarterMoonFace: "\u{1F31B}",
|
|
541
|
-
fish: "\u{1F41F}",
|
|
542
|
-
fishCakeWithSwirl: "\u{1F365}",
|
|
543
|
-
fishingPole: "\u{1F3A3}",
|
|
544
|
-
fiveOClock: "\u{1F554}",
|
|
545
|
-
fiveThirty: "\u{1F560}",
|
|
546
|
-
flagAfghanistan: "\u{1F1E6}\u{1F1EB}",
|
|
547
|
-
flagAlandIslands: "\u{1F1E6}\u{1F1FD}",
|
|
548
|
-
flagAlbania: "\u{1F1E6}\u{1F1F1}",
|
|
549
|
-
flagAlgeria: "\u{1F1E9}\u{1F1FF}",
|
|
550
|
-
flagAmericanSamoa: "\u{1F1E6}\u{1F1F8}",
|
|
551
|
-
flagAndorra: "\u{1F1E6}\u{1F1E9}",
|
|
552
|
-
flagAngola: "\u{1F1E6}\u{1F1F4}",
|
|
553
|
-
flagAnguilla: "\u{1F1E6}\u{1F1EE}",
|
|
554
|
-
flagAntarctica: "\u{1F1E6}\u{1F1F6}",
|
|
555
|
-
flagAntiguaBarbuda: "\u{1F1E6}\u{1F1EC}",
|
|
556
|
-
flagArgentina: "\u{1F1E6}\u{1F1F7}",
|
|
557
|
-
flagArmenia: "\u{1F1E6}\u{1F1F2}",
|
|
558
|
-
flagAruba: "\u{1F1E6}\u{1F1FC}",
|
|
559
|
-
flagAscensionIsland: "\u{1F1E6}\u{1F1E8}",
|
|
560
|
-
flagAustralia: "\u{1F1E6}\u{1F1FA}",
|
|
561
|
-
flagAustria: "\u{1F1E6}\u{1F1F9}",
|
|
562
|
-
flagAzerbaijan: "\u{1F1E6}\u{1F1FF}",
|
|
563
|
-
flagBahamas: "\u{1F1E7}\u{1F1F8}",
|
|
564
|
-
flagBahrain: "\u{1F1E7}\u{1F1ED}",
|
|
565
|
-
flagBangladesh: "\u{1F1E7}\u{1F1E9}",
|
|
566
|
-
flagBarbados: "\u{1F1E7}\u{1F1E7}",
|
|
567
|
-
flagBelarus: "\u{1F1E7}\u{1F1FE}",
|
|
568
|
-
flagBelgium: "\u{1F1E7}\u{1F1EA}",
|
|
569
|
-
flagBelize: "\u{1F1E7}\u{1F1FF}",
|
|
570
|
-
flagBenin: "\u{1F1E7}\u{1F1EF}",
|
|
571
|
-
flagBermuda: "\u{1F1E7}\u{1F1F2}",
|
|
572
|
-
flagBhutan: "\u{1F1E7}\u{1F1F9}",
|
|
573
|
-
flagBolivia: "\u{1F1E7}\u{1F1F4}",
|
|
574
|
-
flagBosniaHerzegovina: "\u{1F1E7}\u{1F1E6}",
|
|
575
|
-
flagBotswana: "\u{1F1E7}\u{1F1FC}",
|
|
576
|
-
flagBouvetIsland: "\u{1F1E7}\u{1F1FB}",
|
|
577
|
-
flagBrazil: "\u{1F1E7}\u{1F1F7}",
|
|
578
|
-
flagBritishIndianOceanTerritory: "\u{1F1EE}\u{1F1F4}",
|
|
579
|
-
flagBritishVirginIslands: "\u{1F1FB}\u{1F1EC}",
|
|
580
|
-
flagBrunei: "\u{1F1E7}\u{1F1F3}",
|
|
581
|
-
flagBulgaria: "\u{1F1E7}\u{1F1EC}",
|
|
582
|
-
flagBurkinaFaso: "\u{1F1E7}\u{1F1EB}",
|
|
583
|
-
flagBurundi: "\u{1F1E7}\u{1F1EE}",
|
|
584
|
-
flagCambodia: "\u{1F1F0}\u{1F1ED}",
|
|
585
|
-
flagCameroon: "\u{1F1E8}\u{1F1F2}",
|
|
586
|
-
flagCanada: "\u{1F1E8}\u{1F1E6}",
|
|
587
|
-
flagCanaryIslands: "\u{1F1EE}\u{1F1E8}",
|
|
588
|
-
flagCapeVerde: "\u{1F1E8}\u{1F1FB}",
|
|
589
|
-
flagCaribbeanNetherlands: "\u{1F1E7}\u{1F1F6}",
|
|
590
|
-
flagCaymanIslands: "\u{1F1F0}\u{1F1FE}",
|
|
591
|
-
flagCentralAfricanRepublic: "\u{1F1E8}\u{1F1EB}",
|
|
592
|
-
flagCeutaMelilla: "\u{1F1EA}\u{1F1E6}",
|
|
593
|
-
flagChad: "\u{1F1F9}\u{1F1E9}",
|
|
594
|
-
flagChile: "\u{1F1E8}\u{1F1F1}",
|
|
595
|
-
flagChina: "\u{1F1E8}\u{1F1F3}",
|
|
596
|
-
flagChristmasIsland: "\u{1F1E8}\u{1F1FD}",
|
|
597
|
-
flagClippertonIsland: "\u{1F1E8}\u{1F1F5}",
|
|
598
|
-
flagCocosIslands: "\u{1F1E8}\u{1F1E8}",
|
|
599
|
-
flagColombia: "\u{1F1E8}\u{1F1F4}",
|
|
600
|
-
flagComoros: "\u{1F1F0}\u{1F1F2}",
|
|
601
|
-
flagCongoBrazzaville: "\u{1F1E8}\u{1F1EC}",
|
|
602
|
-
flagCongoKinshasa: "\u{1F1E8}\u{1F1E9}",
|
|
603
|
-
flagCookIslands: "\u{1F1E8}\u{1F1F0}",
|
|
604
|
-
flagCostaRica: "\u{1F1E8}\u{1F1F7}",
|
|
605
|
-
flagCoteDIvoire: "\u{1F1E8}\u{1F1EE}",
|
|
606
|
-
flagCroatia: "\u{1F1ED}\u{1F1F7}",
|
|
607
|
-
flagCuba: "\u{1F1E8}\u{1F1FA}",
|
|
608
|
-
flagCuracao: "\u{1F1E8}\u{1F1FC}",
|
|
609
|
-
flagCyprus: "\u{1F1E8}\u{1F1FE}",
|
|
610
|
-
flagCzechia: "\u{1F1E8}\u{1F1FF}",
|
|
611
|
-
flagDenmark: "\u{1F1E9}\u{1F1F0}",
|
|
612
|
-
flagDiegoGarcia: "\u{1F1E9}\u{1F1EC}",
|
|
613
|
-
flagDjibouti: "\u{1F1E9}\u{1F1EF}",
|
|
614
|
-
flagDominica: "\u{1F1E9}\u{1F1F2}",
|
|
615
|
-
flagDominicanRepublic: "\u{1F1E9}\u{1F1F4}",
|
|
616
|
-
flagEcuador: "\u{1F1EA}\u{1F1E8}",
|
|
617
|
-
flagEgypt: "\u{1F1EA}\u{1F1EC}",
|
|
618
|
-
flagElSalvador: "\u{1F1F8}\u{1F1FB}",
|
|
619
|
-
flagEngland: "\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}",
|
|
620
|
-
flagEquatorialGuinea: "\u{1F1EC}\u{1F1F6}",
|
|
621
|
-
flagEritrea: "\u{1F1EA}\u{1F1F7}",
|
|
622
|
-
flagEstonia: "\u{1F1EA}\u{1F1EA}",
|
|
623
|
-
flagEswatini: "\u{1F1F8}\u{1F1FF}",
|
|
624
|
-
flagEthiopia: "\u{1F1EA}\u{1F1F9}",
|
|
625
|
-
flagEuropeanUnion: "\u{1F1EA}\u{1F1FA}",
|
|
626
|
-
flagFalklandIslands: "\u{1F1EB}\u{1F1F0}",
|
|
627
|
-
flagFaroeIslands: "\u{1F1EB}\u{1F1F4}",
|
|
628
|
-
flagFiji: "\u{1F1EB}\u{1F1EF}",
|
|
629
|
-
flagFinland: "\u{1F1EB}\u{1F1EE}",
|
|
630
|
-
flagFrance: "\u{1F1EB}\u{1F1F7}",
|
|
631
|
-
flagFrenchGuiana: "\u{1F1EC}\u{1F1EB}",
|
|
632
|
-
flagFrenchPolynesia: "\u{1F1F5}\u{1F1EB}",
|
|
633
|
-
flagFrenchSouthernTerritories: "\u{1F1F9}\u{1F1EB}",
|
|
634
|
-
flagGabon: "\u{1F1EC}\u{1F1E6}",
|
|
635
|
-
flagGambia: "\u{1F1EC}\u{1F1F2}",
|
|
636
|
-
flagGeorgia: "\u{1F1EC}\u{1F1EA}",
|
|
637
|
-
flagGermany: "\u{1F1E9}\u{1F1EA}",
|
|
638
|
-
flagGhana: "\u{1F1EC}\u{1F1ED}",
|
|
639
|
-
flagGibraltar: "\u{1F1EC}\u{1F1EE}",
|
|
640
|
-
flagGreece: "\u{1F1EC}\u{1F1F7}",
|
|
641
|
-
flagGreenland: "\u{1F1EC}\u{1F1F1}",
|
|
642
|
-
flagGrenada: "\u{1F1EC}\u{1F1E9}",
|
|
643
|
-
flagGuadeloupe: "\u{1F1EC}\u{1F1F5}",
|
|
644
|
-
flagGuam: "\u{1F1EC}\u{1F1FA}",
|
|
645
|
-
flagGuatemala: "\u{1F1EC}\u{1F1F9}",
|
|
646
|
-
flagGuernsey: "\u{1F1EC}\u{1F1EC}",
|
|
647
|
-
flagGuinea: "\u{1F1EC}\u{1F1F3}",
|
|
648
|
-
flagGuineaBissau: "\u{1F1EC}\u{1F1FC}",
|
|
649
|
-
flagGuyana: "\u{1F1EC}\u{1F1FE}",
|
|
650
|
-
flagHaiti: "\u{1F1ED}\u{1F1F9}",
|
|
651
|
-
flagHeardMcdonaldIslands: "\u{1F1ED}\u{1F1F2}",
|
|
652
|
-
flagHonduras: "\u{1F1ED}\u{1F1F3}",
|
|
653
|
-
flagHongKongSarChina: "\u{1F1ED}\u{1F1F0}",
|
|
654
|
-
flagHungary: "\u{1F1ED}\u{1F1FA}",
|
|
655
|
-
flagIceland: "\u{1F1EE}\u{1F1F8}",
|
|
656
|
-
flagIndia: "\u{1F1EE}\u{1F1F3}",
|
|
657
|
-
flagIndonesia: "\u{1F1EE}\u{1F1E9}",
|
|
658
|
-
flagInHole: "\u26F3",
|
|
659
|
-
flagIran: "\u{1F1EE}\u{1F1F7}",
|
|
660
|
-
flagIraq: "\u{1F1EE}\u{1F1F6}",
|
|
661
|
-
flagIreland: "\u{1F1EE}\u{1F1EA}",
|
|
662
|
-
flagIsleOfMan: "\u{1F1EE}\u{1F1F2}",
|
|
663
|
-
flagIsrael: "\u{1F1EE}\u{1F1F1}",
|
|
664
|
-
flagItaly: "\u{1F1EE}\u{1F1F9}",
|
|
665
|
-
flagJamaica: "\u{1F1EF}\u{1F1F2}",
|
|
666
|
-
flagJapan: "\u{1F1EF}\u{1F1F5}",
|
|
667
|
-
flagJersey: "\u{1F1EF}\u{1F1EA}",
|
|
668
|
-
flagJordan: "\u{1F1EF}\u{1F1F4}",
|
|
669
|
-
flagKazakhstan: "\u{1F1F0}\u{1F1FF}",
|
|
670
|
-
flagKenya: "\u{1F1F0}\u{1F1EA}",
|
|
671
|
-
flagKiribati: "\u{1F1F0}\u{1F1EE}",
|
|
672
|
-
flagKosovo: "\u{1F1FD}\u{1F1F0}",
|
|
673
|
-
flagKuwait: "\u{1F1F0}\u{1F1FC}",
|
|
674
|
-
flagKyrgyzstan: "\u{1F1F0}\u{1F1EC}",
|
|
675
|
-
flagLaos: "\u{1F1F1}\u{1F1E6}",
|
|
676
|
-
flagLatvia: "\u{1F1F1}\u{1F1FB}",
|
|
677
|
-
flagLebanon: "\u{1F1F1}\u{1F1E7}",
|
|
678
|
-
flagLesotho: "\u{1F1F1}\u{1F1F8}",
|
|
679
|
-
flagLiberia: "\u{1F1F1}\u{1F1F7}",
|
|
680
|
-
flagLibya: "\u{1F1F1}\u{1F1FE}",
|
|
681
|
-
flagLiechtenstein: "\u{1F1F1}\u{1F1EE}",
|
|
682
|
-
flagLithuania: "\u{1F1F1}\u{1F1F9}",
|
|
683
|
-
flagLuxembourg: "\u{1F1F1}\u{1F1FA}",
|
|
684
|
-
flagMacaoSarChina: "\u{1F1F2}\u{1F1F4}",
|
|
685
|
-
flagMadagascar: "\u{1F1F2}\u{1F1EC}",
|
|
686
|
-
flagMalawi: "\u{1F1F2}\u{1F1FC}",
|
|
687
|
-
flagMalaysia: "\u{1F1F2}\u{1F1FE}",
|
|
688
|
-
flagMaldives: "\u{1F1F2}\u{1F1FB}",
|
|
689
|
-
flagMali: "\u{1F1F2}\u{1F1F1}",
|
|
690
|
-
flagMalta: "\u{1F1F2}\u{1F1F9}",
|
|
691
|
-
flagMarshallIslands: "\u{1F1F2}\u{1F1ED}",
|
|
692
|
-
flagMartinique: "\u{1F1F2}\u{1F1F6}",
|
|
693
|
-
flagMauritania: "\u{1F1F2}\u{1F1F7}",
|
|
694
|
-
flagMauritius: "\u{1F1F2}\u{1F1FA}",
|
|
695
|
-
flagMayotte: "\u{1F1FE}\u{1F1F9}",
|
|
696
|
-
flagMexico: "\u{1F1F2}\u{1F1FD}",
|
|
697
|
-
flagMicronesia: "\u{1F1EB}\u{1F1F2}",
|
|
698
|
-
flagMoldova: "\u{1F1F2}\u{1F1E9}",
|
|
699
|
-
flagMonaco: "\u{1F1F2}\u{1F1E8}",
|
|
700
|
-
flagMongolia: "\u{1F1F2}\u{1F1F3}",
|
|
701
|
-
flagMontenegro: "\u{1F1F2}\u{1F1EA}",
|
|
702
|
-
flagMontserrat: "\u{1F1F2}\u{1F1F8}",
|
|
703
|
-
flagMorocco: "\u{1F1F2}\u{1F1E6}",
|
|
704
|
-
flagMozambique: "\u{1F1F2}\u{1F1FF}",
|
|
705
|
-
flagMyanmar: "\u{1F1F2}\u{1F1F2}",
|
|
706
|
-
flagNamibia: "\u{1F1F3}\u{1F1E6}",
|
|
707
|
-
flagNauru: "\u{1F1F3}\u{1F1F7}",
|
|
708
|
-
flagNepal: "\u{1F1F3}\u{1F1F5}",
|
|
709
|
-
flagNetherlands: "\u{1F1F3}\u{1F1F1}",
|
|
710
|
-
flagNewCaledonia: "\u{1F1F3}\u{1F1E8}",
|
|
711
|
-
flagNewZealand: "\u{1F1F3}\u{1F1FF}",
|
|
712
|
-
flagNicaragua: "\u{1F1F3}\u{1F1EE}",
|
|
713
|
-
flagNiger: "\u{1F1F3}\u{1F1EA}",
|
|
714
|
-
flagNigeria: "\u{1F1F3}\u{1F1EC}",
|
|
715
|
-
flagNiue: "\u{1F1F3}\u{1F1FA}",
|
|
716
|
-
flagNorfolkIsland: "\u{1F1F3}\u{1F1EB}",
|
|
717
|
-
flagNorthernMarianaIslands: "\u{1F1F2}\u{1F1F5}",
|
|
718
|
-
flagNorthKorea: "\u{1F1F0}\u{1F1F5}",
|
|
719
|
-
flagNorthMacedonia: "\u{1F1F2}\u{1F1F0}",
|
|
720
|
-
flagNorway: "\u{1F1F3}\u{1F1F4}",
|
|
721
|
-
flagOman: "\u{1F1F4}\u{1F1F2}",
|
|
722
|
-
flagPakistan: "\u{1F1F5}\u{1F1F0}",
|
|
723
|
-
flagPalau: "\u{1F1F5}\u{1F1FC}",
|
|
724
|
-
flagPalestinianTerritories: "\u{1F1F5}\u{1F1F8}",
|
|
725
|
-
flagPanama: "\u{1F1F5}\u{1F1E6}",
|
|
726
|
-
flagPapuaNewGuinea: "\u{1F1F5}\u{1F1EC}",
|
|
727
|
-
flagParaguay: "\u{1F1F5}\u{1F1FE}",
|
|
728
|
-
flagPeru: "\u{1F1F5}\u{1F1EA}",
|
|
729
|
-
flagPhilippines: "\u{1F1F5}\u{1F1ED}",
|
|
730
|
-
flagPitcairnIslands: "\u{1F1F5}\u{1F1F3}",
|
|
731
|
-
flagPoland: "\u{1F1F5}\u{1F1F1}",
|
|
732
|
-
flagPortugal: "\u{1F1F5}\u{1F1F9}",
|
|
733
|
-
flagPuertoRico: "\u{1F1F5}\u{1F1F7}",
|
|
734
|
-
flagQatar: "\u{1F1F6}\u{1F1E6}",
|
|
735
|
-
flagReunion: "\u{1F1F7}\u{1F1EA}",
|
|
736
|
-
flagRomania: "\u{1F1F7}\u{1F1F4}",
|
|
737
|
-
flagRussia: "\u{1F1F7}\u{1F1FA}",
|
|
738
|
-
flagRwanda: "\u{1F1F7}\u{1F1FC}",
|
|
739
|
-
flagSamoa: "\u{1F1FC}\u{1F1F8}",
|
|
740
|
-
flagSanMarino: "\u{1F1F8}\u{1F1F2}",
|
|
741
|
-
flagSaoTomePrincipe: "\u{1F1F8}\u{1F1F9}",
|
|
742
|
-
flagSark: "\u{1F1E8}\u{1F1F6}",
|
|
743
|
-
flagSaudiArabia: "\u{1F1F8}\u{1F1E6}",
|
|
744
|
-
flagScotland: "\u{1F3F4}\u{E0067}\u{E0062}\u{E0073}\u{E0063}\u{E0074}\u{E007F}",
|
|
745
|
-
flagSenegal: "\u{1F1F8}\u{1F1F3}",
|
|
746
|
-
flagSerbia: "\u{1F1F7}\u{1F1F8}",
|
|
747
|
-
flagSeychelles: "\u{1F1F8}\u{1F1E8}",
|
|
748
|
-
flagSierraLeone: "\u{1F1F8}\u{1F1F1}",
|
|
749
|
-
flagSingapore: "\u{1F1F8}\u{1F1EC}",
|
|
750
|
-
flagSintMaarten: "\u{1F1F8}\u{1F1FD}",
|
|
751
|
-
flagSlovakia: "\u{1F1F8}\u{1F1F0}",
|
|
752
|
-
flagSlovenia: "\u{1F1F8}\u{1F1EE}",
|
|
753
|
-
flagSolomonIslands: "\u{1F1F8}\u{1F1E7}",
|
|
754
|
-
flagSomalia: "\u{1F1F8}\u{1F1F4}",
|
|
755
|
-
flagSouthAfrica: "\u{1F1FF}\u{1F1E6}",
|
|
756
|
-
flagSouthGeorgiaSouthSandwichIslands: "\u{1F1EC}\u{1F1F8}",
|
|
757
|
-
flagSouthKorea: "\u{1F1F0}\u{1F1F7}",
|
|
758
|
-
flagSouthSudan: "\u{1F1F8}\u{1F1F8}",
|
|
759
|
-
flagSpain: "\u{1F1EA}\u{1F1F8}",
|
|
760
|
-
flagSriLanka: "\u{1F1F1}\u{1F1F0}",
|
|
761
|
-
flagStBarthelemy: "\u{1F1E7}\u{1F1F1}",
|
|
762
|
-
flagStHelena: "\u{1F1F8}\u{1F1ED}",
|
|
763
|
-
flagStKittsNevis: "\u{1F1F0}\u{1F1F3}",
|
|
764
|
-
flagStLucia: "\u{1F1F1}\u{1F1E8}",
|
|
765
|
-
flagStMartin: "\u{1F1F2}\u{1F1EB}",
|
|
766
|
-
flagStPierreMiquelon: "\u{1F1F5}\u{1F1F2}",
|
|
767
|
-
flagStVincentGrenadines: "\u{1F1FB}\u{1F1E8}",
|
|
768
|
-
flagSudan: "\u{1F1F8}\u{1F1E9}",
|
|
769
|
-
flagSuriname: "\u{1F1F8}\u{1F1F7}",
|
|
770
|
-
flagSvalbardJanMayen: "\u{1F1F8}\u{1F1EF}",
|
|
771
|
-
flagSweden: "\u{1F1F8}\u{1F1EA}",
|
|
772
|
-
flagSwitzerland: "\u{1F1E8}\u{1F1ED}",
|
|
773
|
-
flagSyria: "\u{1F1F8}\u{1F1FE}",
|
|
774
|
-
flagTaiwan: "\u{1F1F9}\u{1F1FC}",
|
|
775
|
-
flagTajikistan: "\u{1F1F9}\u{1F1EF}",
|
|
776
|
-
flagTanzania: "\u{1F1F9}\u{1F1FF}",
|
|
777
|
-
flagThailand: "\u{1F1F9}\u{1F1ED}",
|
|
778
|
-
flagTimorLeste: "\u{1F1F9}\u{1F1F1}",
|
|
779
|
-
flagTogo: "\u{1F1F9}\u{1F1EC}",
|
|
780
|
-
flagTokelau: "\u{1F1F9}\u{1F1F0}",
|
|
781
|
-
flagTonga: "\u{1F1F9}\u{1F1F4}",
|
|
782
|
-
flagTrinidadTobago: "\u{1F1F9}\u{1F1F9}",
|
|
783
|
-
flagTristanDaCunha: "\u{1F1F9}\u{1F1E6}",
|
|
784
|
-
flagTunisia: "\u{1F1F9}\u{1F1F3}",
|
|
785
|
-
flagTurkiye: "\u{1F1F9}\u{1F1F7}",
|
|
786
|
-
flagTurkmenistan: "\u{1F1F9}\u{1F1F2}",
|
|
787
|
-
flagTurksCaicosIslands: "\u{1F1F9}\u{1F1E8}",
|
|
788
|
-
flagTuvalu: "\u{1F1F9}\u{1F1FB}",
|
|
789
|
-
flagUganda: "\u{1F1FA}\u{1F1EC}",
|
|
790
|
-
flagUkraine: "\u{1F1FA}\u{1F1E6}",
|
|
791
|
-
flagUnitedArabEmirates: "\u{1F1E6}\u{1F1EA}",
|
|
792
|
-
flagUnitedKingdom: "\u{1F1EC}\u{1F1E7}",
|
|
793
|
-
flagUnitedNations: "\u{1F1FA}\u{1F1F3}",
|
|
794
|
-
flagUnitedStates: "\u{1F1FA}\u{1F1F8}",
|
|
795
|
-
flagUruguay: "\u{1F1FA}\u{1F1FE}",
|
|
796
|
-
flagUSOutlyingIslands: "\u{1F1FA}\u{1F1F2}",
|
|
797
|
-
flagUSVirginIslands: "\u{1F1FB}\u{1F1EE}",
|
|
798
|
-
flagUzbekistan: "\u{1F1FA}\u{1F1FF}",
|
|
799
|
-
flagVanuatu: "\u{1F1FB}\u{1F1FA}",
|
|
800
|
-
flagVaticanCity: "\u{1F1FB}\u{1F1E6}",
|
|
801
|
-
flagVenezuela: "\u{1F1FB}\u{1F1EA}",
|
|
802
|
-
flagVietnam: "\u{1F1FB}\u{1F1F3}",
|
|
803
|
-
flagWales: "\u{1F3F4}\u{E0067}\u{E0062}\u{E0077}\u{E006C}\u{E0073}\u{E007F}",
|
|
804
|
-
flagWallisFutuna: "\u{1F1FC}\u{1F1EB}",
|
|
805
|
-
flagWesternSahara: "\u{1F1EA}\u{1F1ED}",
|
|
806
|
-
flagYemen: "\u{1F1FE}\u{1F1EA}",
|
|
807
|
-
flagZambia: "\u{1F1FF}\u{1F1F2}",
|
|
808
|
-
flagZimbabwe: "\u{1F1FF}\u{1F1FC}",
|
|
809
|
-
flamingo: "\u{1F9A9}",
|
|
810
|
-
flashlight: "\u{1F526}",
|
|
811
|
-
flatbread: "\u{1FAD3}",
|
|
812
|
-
flatShoe: "\u{1F97F}",
|
|
813
|
-
fleurDeLis: "\u269C\uFE0F",
|
|
814
|
-
flexedBiceps: "\u{1F4AA}",
|
|
815
|
-
floppyDisk: "\u{1F4BE}",
|
|
816
|
-
flowerPlayingCards: "\u{1F3B4}",
|
|
817
|
-
flushedFace: "\u{1F633}",
|
|
818
|
-
flute: "\u{1FA88}",
|
|
819
|
-
fly: "\u{1FAB0}",
|
|
820
|
-
flyingDisc: "\u{1F94F}",
|
|
821
|
-
flyingSaucer: "\u{1F6F8}",
|
|
822
|
-
fog: "\u{1F32B}\uFE0F",
|
|
823
|
-
foggy: "\u{1F301}",
|
|
824
|
-
foldedHands: "\u{1F64F}",
|
|
825
|
-
foldingHandFan: "\u{1FAAD}",
|
|
826
|
-
fondue: "\u{1FAD5}",
|
|
827
|
-
foot: "\u{1F9B6}",
|
|
828
|
-
footprints: "\u{1F463}",
|
|
829
|
-
forkAndKnife: "\u{1F374}",
|
|
830
|
-
forkAndKnifeWithPlate: "\u{1F37D}\uFE0F",
|
|
831
|
-
fortuneCookie: "\u{1F960}",
|
|
832
|
-
fountain: "\u26F2",
|
|
833
|
-
fountainPen: "\u{1F58B}\uFE0F",
|
|
834
|
-
fourLeafClover: "\u{1F340}",
|
|
835
|
-
fourOClock: "\u{1F553}",
|
|
836
|
-
fourThirty: "\u{1F55F}",
|
|
837
|
-
fox: "\u{1F98A}",
|
|
838
|
-
framedPicture: "\u{1F5BC}\uFE0F",
|
|
839
|
-
freeButton: "\u{1F193}",
|
|
840
|
-
frenchFries: "\u{1F35F}",
|
|
841
|
-
friedShrimp: "\u{1F364}",
|
|
842
|
-
frog: "\u{1F438}",
|
|
843
|
-
frontFacingBabyChick: "\u{1F425}",
|
|
844
|
-
frowningFace: "\u2639\uFE0F",
|
|
845
|
-
frowningFaceWithOpenMouth: "\u{1F626}",
|
|
846
|
-
fuelPump: "\u26FD",
|
|
847
|
-
fullMoon: "\u{1F315}",
|
|
848
|
-
fullMoonFace: "\u{1F31D}",
|
|
849
|
-
funeralUrn: "\u26B1\uFE0F",
|
|
850
|
-
gameDie: "\u{1F3B2}",
|
|
851
|
-
garlic: "\u{1F9C4}",
|
|
852
|
-
gear: "\u2699\uFE0F",
|
|
853
|
-
gemini: "\u264A",
|
|
854
|
-
gemStone: "\u{1F48E}",
|
|
855
|
-
genie: "\u{1F9DE}",
|
|
856
|
-
ghost: "\u{1F47B}",
|
|
857
|
-
gingerRoot: "\u{1FADA}",
|
|
858
|
-
giraffe: "\u{1F992}",
|
|
859
|
-
girl: "\u{1F467}",
|
|
860
|
-
glasses: "\u{1F453}",
|
|
861
|
-
glassOfMilk: "\u{1F95B}",
|
|
862
|
-
globeShowingAmericas: "\u{1F30E}",
|
|
863
|
-
globeShowingAsiaAustralia: "\u{1F30F}",
|
|
864
|
-
globeShowingEuropeAfrica: "\u{1F30D}",
|
|
865
|
-
globeWithMeridians: "\u{1F310}",
|
|
866
|
-
gloves: "\u{1F9E4}",
|
|
867
|
-
glowingStar: "\u{1F31F}",
|
|
868
|
-
goalNet: "\u{1F945}",
|
|
869
|
-
goat: "\u{1F410}",
|
|
870
|
-
goblin: "\u{1F47A}",
|
|
871
|
-
goggles: "\u{1F97D}",
|
|
872
|
-
goose: "\u{1FABF}",
|
|
873
|
-
gorilla: "\u{1F98D}",
|
|
874
|
-
graduationCap: "\u{1F393}",
|
|
875
|
-
grapes: "\u{1F347}",
|
|
876
|
-
greenApple: "\u{1F34F}",
|
|
877
|
-
greenBook: "\u{1F4D7}",
|
|
878
|
-
greenCircle: "\u{1F7E2}",
|
|
879
|
-
greenHeart: "\u{1F49A}",
|
|
880
|
-
greenSalad: "\u{1F957}",
|
|
881
|
-
greenSquare: "\u{1F7E9}",
|
|
882
|
-
greyHeart: "\u{1FA76}",
|
|
883
|
-
grimacingFace: "\u{1F62C}",
|
|
884
|
-
grinningCat: "\u{1F63A}",
|
|
885
|
-
grinningCatWithSmilingEyes: "\u{1F638}",
|
|
886
|
-
grinningFace: "\u{1F600}",
|
|
887
|
-
grinningFaceWithBigEyes: "\u{1F603}",
|
|
888
|
-
grinningFaceWithSmilingEyes: "\u{1F604}",
|
|
889
|
-
grinningFaceWithSweat: "\u{1F605}",
|
|
890
|
-
grinningSquintingFace: "\u{1F606}",
|
|
891
|
-
growingHeart: "\u{1F497}",
|
|
892
|
-
guard: "\u{1F482}",
|
|
893
|
-
guideDog: "\u{1F9AE}",
|
|
894
|
-
guitar: "\u{1F3B8}",
|
|
895
|
-
hairPick: "\u{1FAAE}",
|
|
896
|
-
hairyCreature: "\u{1FAC8}",
|
|
897
|
-
hamburger: "\u{1F354}",
|
|
898
|
-
hammer: "\u{1F528}",
|
|
899
|
-
hammerAndPick: "\u2692\uFE0F",
|
|
900
|
-
hammerAndWrench: "\u{1F6E0}\uFE0F",
|
|
901
|
-
hamsa: "\u{1FAAC}",
|
|
902
|
-
hamster: "\u{1F439}",
|
|
903
|
-
handbag: "\u{1F45C}",
|
|
904
|
-
handshake: "\u{1F91D}",
|
|
905
|
-
handWithFingersSplayed: "\u{1F590}\uFE0F",
|
|
906
|
-
handWithIndexFingerAndThumbCrossed: "\u{1FAF0}",
|
|
907
|
-
harp: "\u{1FA89}",
|
|
908
|
-
hatchingChick: "\u{1F423}",
|
|
909
|
-
headphone: "\u{1F3A7}",
|
|
910
|
-
headShakingHorizontally: "\u{1F642}\u200D\u2194\uFE0F",
|
|
911
|
-
headShakingVertically: "\u{1F642}\u200D\u2195\uFE0F",
|
|
912
|
-
headstone: "\u{1FAA6}",
|
|
913
|
-
healthWorker: "\u{1F9D1}\u200D\u2695\uFE0F",
|
|
914
|
-
hearNoEvilMonkey: "\u{1F649}",
|
|
915
|
-
heartDecoration: "\u{1F49F}",
|
|
916
|
-
heartExclamation: "\u2763\uFE0F",
|
|
917
|
-
heartHands: "\u{1FAF6}",
|
|
918
|
-
heartOnFire: "\u2764\uFE0F\u200D\u{1F525}",
|
|
919
|
-
heartSuit: "\u2665\uFE0F",
|
|
920
|
-
heartWithArrow: "\u{1F498}",
|
|
921
|
-
heartWithRibbon: "\u{1F49D}",
|
|
922
|
-
heavyDollarSign: "\u{1F4B2}",
|
|
923
|
-
heavyEqualsSign: "\u{1F7F0}",
|
|
924
|
-
hedgehog: "\u{1F994}",
|
|
925
|
-
helicopter: "\u{1F681}",
|
|
926
|
-
herb: "\u{1F33F}",
|
|
927
|
-
hibiscus: "\u{1F33A}",
|
|
928
|
-
highHeeledShoe: "\u{1F460}",
|
|
929
|
-
highSpeedTrain: "\u{1F684}",
|
|
930
|
-
highVoltage: "\u26A1",
|
|
931
|
-
hikingBoot: "\u{1F97E}",
|
|
932
|
-
hinduTemple: "\u{1F6D5}",
|
|
933
|
-
hippopotamus: "\u{1F99B}",
|
|
934
|
-
hole: "\u{1F573}\uFE0F",
|
|
935
|
-
hollowRedCircle: "\u2B55",
|
|
936
|
-
honeybee: "\u{1F41D}",
|
|
937
|
-
honeyPot: "\u{1F36F}",
|
|
938
|
-
hook: "\u{1FA9D}",
|
|
939
|
-
horizontalTrafficLight: "\u{1F6A5}",
|
|
940
|
-
horse: "\u{1F40E}",
|
|
941
|
-
horseFace: "\u{1F434}",
|
|
942
|
-
horseRacing: "\u{1F3C7}",
|
|
943
|
-
hospital: "\u{1F3E5}",
|
|
944
|
-
hotBeverage: "\u2615",
|
|
945
|
-
hotDog: "\u{1F32D}",
|
|
946
|
-
hotel: "\u{1F3E8}",
|
|
947
|
-
hotFace: "\u{1F975}",
|
|
948
|
-
hotPepper: "\u{1F336}\uFE0F",
|
|
949
|
-
hotSprings: "\u2668\uFE0F",
|
|
950
|
-
hourglassDone: "\u231B",
|
|
951
|
-
hourglassNotDone: "\u23F3",
|
|
952
|
-
house: "\u{1F3E0}",
|
|
953
|
-
houses: "\u{1F3D8}\uFE0F",
|
|
954
|
-
houseWithGarden: "\u{1F3E1}",
|
|
955
|
-
hundredPoints: "\u{1F4AF}",
|
|
956
|
-
hushedFace: "\u{1F62F}",
|
|
957
|
-
hut: "\u{1F6D6}",
|
|
958
|
-
hyacinth: "\u{1FABB}",
|
|
959
|
-
ice: "\u{1F9CA}",
|
|
960
|
-
iceCream: "\u{1F368}",
|
|
961
|
-
iceHockey: "\u{1F3D2}",
|
|
962
|
-
iceSkate: "\u26F8\uFE0F",
|
|
963
|
-
idButton: "\u{1F194}",
|
|
964
|
-
identificationCard: "\u{1FAAA}",
|
|
965
|
-
inboxTray: "\u{1F4E5}",
|
|
966
|
-
incomingEnvelope: "\u{1F4E8}",
|
|
967
|
-
indexPointingAtTheViewer: "\u{1FAF5}",
|
|
968
|
-
indexPointingUp: "\u261D\uFE0F",
|
|
969
|
-
infinity: "\u267E\uFE0F",
|
|
970
|
-
information: "\u2139\uFE0F",
|
|
971
|
-
inputLatinLetters: "\u{1F524}",
|
|
972
|
-
inputLatinLowercase: "\u{1F521}",
|
|
973
|
-
inputLatinUppercase: "\u{1F520}",
|
|
974
|
-
inputNumbers: "\u{1F522}",
|
|
975
|
-
inputSymbols: "\u{1F523}",
|
|
976
|
-
jackOLantern: "\u{1F383}",
|
|
977
|
-
japaneseAcceptableButton: "\u{1F251}",
|
|
978
|
-
japaneseApplicationButton: "\u{1F238}",
|
|
979
|
-
japaneseBargainButton: "\u{1F250}",
|
|
980
|
-
japaneseCastle: "\u{1F3EF}",
|
|
981
|
-
japaneseCongratulationsButton: "\u3297\uFE0F",
|
|
982
|
-
japaneseDiscountButton: "\u{1F239}",
|
|
983
|
-
japaneseDolls: "\u{1F38E}",
|
|
984
|
-
japaneseFreeOfChargeButton: "\u{1F21A}",
|
|
985
|
-
japaneseHereButton: "\u{1F201}",
|
|
986
|
-
japaneseMonthlyAmountButton: "\u{1F237}\uFE0F",
|
|
987
|
-
japaneseNotFreeOfChargeButton: "\u{1F236}",
|
|
988
|
-
japaneseNoVacancyButton: "\u{1F235}",
|
|
989
|
-
japaneseOpenForBusinessButton: "\u{1F23A}",
|
|
990
|
-
japanesePassingGradeButton: "\u{1F234}",
|
|
991
|
-
japanesePostOffice: "\u{1F3E3}",
|
|
992
|
-
japaneseProhibitedButton: "\u{1F232}",
|
|
993
|
-
japaneseReservedButton: "\u{1F22F}",
|
|
994
|
-
japaneseSecretButton: "\u3299\uFE0F",
|
|
995
|
-
japaneseServiceChargeButton: "\u{1F202}\uFE0F",
|
|
996
|
-
japaneseSymbolForBeginner: "\u{1F530}",
|
|
997
|
-
japaneseVacancyButton: "\u{1F233}",
|
|
998
|
-
jar: "\u{1FAD9}",
|
|
999
|
-
jeans: "\u{1F456}",
|
|
1000
|
-
jellyfish: "\u{1FABC}",
|
|
1001
|
-
joker: "\u{1F0CF}",
|
|
1002
|
-
joystick: "\u{1F579}\uFE0F",
|
|
1003
|
-
judge: "\u{1F9D1}\u200D\u2696\uFE0F",
|
|
1004
|
-
kaaba: "\u{1F54B}",
|
|
1005
|
-
kangaroo: "\u{1F998}",
|
|
1006
|
-
key: "\u{1F511}",
|
|
1007
|
-
keyboard: "\u2328\uFE0F",
|
|
1008
|
-
keycap0: "0\uFE0F\u20E3",
|
|
1009
|
-
keycap1: "1\uFE0F\u20E3",
|
|
1010
|
-
keycap10: "\u{1F51F}",
|
|
1011
|
-
keycap2: "2\uFE0F\u20E3",
|
|
1012
|
-
keycap3: "3\uFE0F\u20E3",
|
|
1013
|
-
keycap4: "4\uFE0F\u20E3",
|
|
1014
|
-
keycap5: "5\uFE0F\u20E3",
|
|
1015
|
-
keycap6: "6\uFE0F\u20E3",
|
|
1016
|
-
keycap7: "7\uFE0F\u20E3",
|
|
1017
|
-
keycap8: "8\uFE0F\u20E3",
|
|
1018
|
-
keycap9: "9\uFE0F\u20E3",
|
|
1019
|
-
keycapAsterisk: "*\uFE0F\u20E3",
|
|
1020
|
-
keycapNumberSign: "#\uFE0F\u20E3",
|
|
1021
|
-
khanda: "\u{1FAAF}",
|
|
1022
|
-
kickScooter: "\u{1F6F4}",
|
|
1023
|
-
kimono: "\u{1F458}",
|
|
1024
|
-
kiss: "\u{1F48F}",
|
|
1025
|
-
kissingCat: "\u{1F63D}",
|
|
1026
|
-
kissingFace: "\u{1F617}",
|
|
1027
|
-
kissingFaceWithClosedEyes: "\u{1F61A}",
|
|
1028
|
-
kissingFaceWithSmilingEyes: "\u{1F619}",
|
|
1029
|
-
kissManMan: "\u{1F468}\u200D\u2764\uFE0F\u200D\u{1F48B}\u200D\u{1F468}",
|
|
1030
|
-
kissMark: "\u{1F48B}",
|
|
1031
|
-
kissWomanMan: "\u{1F469}\u200D\u2764\uFE0F\u200D\u{1F48B}\u200D\u{1F468}",
|
|
1032
|
-
kissWomanWoman: "\u{1F469}\u200D\u2764\uFE0F\u200D\u{1F48B}\u200D\u{1F469}",
|
|
1033
|
-
kitchenKnife: "\u{1F52A}",
|
|
1034
|
-
kite: "\u{1FA81}",
|
|
1035
|
-
kiwiFruit: "\u{1F95D}",
|
|
1036
|
-
knot: "\u{1FAA2}",
|
|
1037
|
-
koala: "\u{1F428}",
|
|
1038
|
-
labCoat: "\u{1F97C}",
|
|
1039
|
-
label: "\u{1F3F7}\uFE0F",
|
|
1040
|
-
lacrosse: "\u{1F94D}",
|
|
1041
|
-
ladder: "\u{1FA9C}",
|
|
1042
|
-
ladyBeetle: "\u{1F41E}",
|
|
1043
|
-
landslide: "\u{1F6D8}",
|
|
1044
|
-
laptop: "\u{1F4BB}",
|
|
1045
|
-
largeBlueDiamond: "\u{1F537}",
|
|
1046
|
-
largeOrangeDiamond: "\u{1F536}",
|
|
1047
|
-
lastQuarterMoon: "\u{1F317}",
|
|
1048
|
-
lastQuarterMoonFace: "\u{1F31C}",
|
|
1049
|
-
lastTrackButton: "\u23EE\uFE0F",
|
|
1050
|
-
latinCross: "\u271D\uFE0F",
|
|
1051
|
-
leafFlutteringInWind: "\u{1F343}",
|
|
1052
|
-
leaflessTree: "\u{1FABE}",
|
|
1053
|
-
leafyGreen: "\u{1F96C}",
|
|
1054
|
-
ledger: "\u{1F4D2}",
|
|
1055
|
-
leftArrow: "\u2B05\uFE0F",
|
|
1056
|
-
leftArrowCurvingRight: "\u21AA\uFE0F",
|
|
1057
|
-
leftFacingFist: "\u{1F91B}",
|
|
1058
|
-
leftLuggage: "\u{1F6C5}",
|
|
1059
|
-
leftRightArrow: "\u2194\uFE0F",
|
|
1060
|
-
leftSpeechBubble: "\u{1F5E8}\uFE0F",
|
|
1061
|
-
leftwardsHand: "\u{1FAF2}",
|
|
1062
|
-
leftwardsPushingHand: "\u{1FAF7}",
|
|
1063
|
-
leg: "\u{1F9B5}",
|
|
1064
|
-
lemon: "\u{1F34B}",
|
|
1065
|
-
leo: "\u264C",
|
|
1066
|
-
leopard: "\u{1F406}",
|
|
1067
|
-
levelSlider: "\u{1F39A}\uFE0F",
|
|
1068
|
-
libra: "\u264E",
|
|
1069
|
-
lightBlueHeart: "\u{1FA75}",
|
|
1070
|
-
lightBulb: "\u{1F4A1}",
|
|
1071
|
-
lightRail: "\u{1F688}",
|
|
1072
|
-
lime: "\u{1F34B}\u200D\u{1F7E9}",
|
|
1073
|
-
link: "\u{1F517}",
|
|
1074
|
-
linkedPaperclips: "\u{1F587}\uFE0F",
|
|
1075
|
-
lion: "\u{1F981}",
|
|
1076
|
-
lipstick: "\u{1F484}",
|
|
1077
|
-
litterInBinSign: "\u{1F6AE}",
|
|
1078
|
-
lizard: "\u{1F98E}",
|
|
1079
|
-
llama: "\u{1F999}",
|
|
1080
|
-
lobster: "\u{1F99E}",
|
|
1081
|
-
locked: "\u{1F512}",
|
|
1082
|
-
lockedWithKey: "\u{1F510}",
|
|
1083
|
-
lockedWithPen: "\u{1F50F}",
|
|
1084
|
-
locomotive: "\u{1F682}",
|
|
1085
|
-
lollipop: "\u{1F36D}",
|
|
1086
|
-
longDrum: "\u{1FA98}",
|
|
1087
|
-
lotionBottle: "\u{1F9F4}",
|
|
1088
|
-
lotus: "\u{1FAB7}",
|
|
1089
|
-
loudlyCryingFace: "\u{1F62D}",
|
|
1090
|
-
loudspeaker: "\u{1F4E2}",
|
|
1091
|
-
loveHotel: "\u{1F3E9}",
|
|
1092
|
-
loveLetter: "\u{1F48C}",
|
|
1093
|
-
loveYouGesture: "\u{1F91F}",
|
|
1094
|
-
lowBattery: "\u{1FAAB}",
|
|
1095
|
-
luggage: "\u{1F9F3}",
|
|
1096
|
-
lungs: "\u{1FAC1}",
|
|
1097
|
-
lyingFace: "\u{1F925}",
|
|
1098
|
-
mage: "\u{1F9D9}",
|
|
1099
|
-
magicWand: "\u{1FA84}",
|
|
1100
|
-
magnet: "\u{1F9F2}",
|
|
1101
|
-
magnifyingGlassTiltedLeft: "\u{1F50D}",
|
|
1102
|
-
magnifyingGlassTiltedRight: "\u{1F50E}",
|
|
1103
|
-
mahjongRedDragon: "\u{1F004}",
|
|
1104
|
-
maleSign: "\u2642\uFE0F",
|
|
1105
|
-
mammoth: "\u{1F9A3}",
|
|
1106
|
-
man: "\u{1F468}",
|
|
1107
|
-
manArtist: "\u{1F468}\u200D\u{1F3A8}",
|
|
1108
|
-
manAstronaut: "\u{1F468}\u200D\u{1F680}",
|
|
1109
|
-
manBald: "\u{1F468}\u200D\u{1F9B2}",
|
|
1110
|
-
manBeard: "\u{1F9D4}\u200D\u2642\uFE0F",
|
|
1111
|
-
manBiking: "\u{1F6B4}\u200D\u2642\uFE0F",
|
|
1112
|
-
manBlondHair: "\u{1F471}\u200D\u2642\uFE0F",
|
|
1113
|
-
manBouncingBall: "\u26F9\uFE0F\u200D\u2642\uFE0F",
|
|
1114
|
-
manBowing: "\u{1F647}\u200D\u2642\uFE0F",
|
|
1115
|
-
manCartwheeling: "\u{1F938}\u200D\u2642\uFE0F",
|
|
1116
|
-
manClimbing: "\u{1F9D7}\u200D\u2642\uFE0F",
|
|
1117
|
-
manConstructionWorker: "\u{1F477}\u200D\u2642\uFE0F",
|
|
1118
|
-
manCook: "\u{1F468}\u200D\u{1F373}",
|
|
1119
|
-
manCurlyHair: "\u{1F468}\u200D\u{1F9B1}",
|
|
1120
|
-
manDancing: "\u{1F57A}",
|
|
1121
|
-
manDetective: "\u{1F575}\uFE0F\u200D\u2642\uFE0F",
|
|
1122
|
-
manElf: "\u{1F9DD}\u200D\u2642\uFE0F",
|
|
1123
|
-
manFacepalming: "\u{1F926}\u200D\u2642\uFE0F",
|
|
1124
|
-
manFactoryWorker: "\u{1F468}\u200D\u{1F3ED}",
|
|
1125
|
-
manFairy: "\u{1F9DA}\u200D\u2642\uFE0F",
|
|
1126
|
-
manFarmer: "\u{1F468}\u200D\u{1F33E}",
|
|
1127
|
-
manFeedingBaby: "\u{1F468}\u200D\u{1F37C}",
|
|
1128
|
-
manFirefighter: "\u{1F468}\u200D\u{1F692}",
|
|
1129
|
-
manFrowning: "\u{1F64D}\u200D\u2642\uFE0F",
|
|
1130
|
-
manGenie: "\u{1F9DE}\u200D\u2642\uFE0F",
|
|
1131
|
-
manGesturingNo: "\u{1F645}\u200D\u2642\uFE0F",
|
|
1132
|
-
manGesturingOk: "\u{1F646}\u200D\u2642\uFE0F",
|
|
1133
|
-
manGettingHaircut: "\u{1F487}\u200D\u2642\uFE0F",
|
|
1134
|
-
manGettingMassage: "\u{1F486}\u200D\u2642\uFE0F",
|
|
1135
|
-
mango: "\u{1F96D}",
|
|
1136
|
-
manGolfing: "\u{1F3CC}\uFE0F\u200D\u2642\uFE0F",
|
|
1137
|
-
manGuard: "\u{1F482}\u200D\u2642\uFE0F",
|
|
1138
|
-
manHealthWorker: "\u{1F468}\u200D\u2695\uFE0F",
|
|
1139
|
-
manInLotusPosition: "\u{1F9D8}\u200D\u2642\uFE0F",
|
|
1140
|
-
manInManualWheelchair: "\u{1F468}\u200D\u{1F9BD}",
|
|
1141
|
-
manInManualWheelchairFacingRight: "\u{1F468}\u200D\u{1F9BD}\u200D\u27A1\uFE0F",
|
|
1142
|
-
manInMotorizedWheelchair: "\u{1F468}\u200D\u{1F9BC}",
|
|
1143
|
-
manInMotorizedWheelchairFacingRight: "\u{1F468}\u200D\u{1F9BC}\u200D\u27A1\uFE0F",
|
|
1144
|
-
manInSteamyRoom: "\u{1F9D6}\u200D\u2642\uFE0F",
|
|
1145
|
-
manInTuxedo: "\u{1F935}\u200D\u2642\uFE0F",
|
|
1146
|
-
manJudge: "\u{1F468}\u200D\u2696\uFE0F",
|
|
1147
|
-
manJuggling: "\u{1F939}\u200D\u2642\uFE0F",
|
|
1148
|
-
manKneeling: "\u{1F9CE}\u200D\u2642\uFE0F",
|
|
1149
|
-
manKneelingFacingRight: "\u{1F9CE}\u200D\u2642\uFE0F\u200D\u27A1\uFE0F",
|
|
1150
|
-
manLiftingWeights: "\u{1F3CB}\uFE0F\u200D\u2642\uFE0F",
|
|
1151
|
-
manMage: "\u{1F9D9}\u200D\u2642\uFE0F",
|
|
1152
|
-
manMechanic: "\u{1F468}\u200D\u{1F527}",
|
|
1153
|
-
manMountainBiking: "\u{1F6B5}\u200D\u2642\uFE0F",
|
|
1154
|
-
manOfficeWorker: "\u{1F468}\u200D\u{1F4BC}",
|
|
1155
|
-
manPilot: "\u{1F468}\u200D\u2708\uFE0F",
|
|
1156
|
-
manPlayingHandball: "\u{1F93E}\u200D\u2642\uFE0F",
|
|
1157
|
-
manPlayingWaterPolo: "\u{1F93D}\u200D\u2642\uFE0F",
|
|
1158
|
-
manPoliceOfficer: "\u{1F46E}\u200D\u2642\uFE0F",
|
|
1159
|
-
manPouting: "\u{1F64E}\u200D\u2642\uFE0F",
|
|
1160
|
-
manRaisingHand: "\u{1F64B}\u200D\u2642\uFE0F",
|
|
1161
|
-
manRedHair: "\u{1F468}\u200D\u{1F9B0}",
|
|
1162
|
-
manRowingBoat: "\u{1F6A3}\u200D\u2642\uFE0F",
|
|
1163
|
-
manRunning: "\u{1F3C3}\u200D\u2642\uFE0F",
|
|
1164
|
-
manRunningFacingRight: "\u{1F3C3}\u200D\u2642\uFE0F\u200D\u27A1\uFE0F",
|
|
1165
|
-
manScientist: "\u{1F468}\u200D\u{1F52C}",
|
|
1166
|
-
manShrugging: "\u{1F937}\u200D\u2642\uFE0F",
|
|
1167
|
-
manSinger: "\u{1F468}\u200D\u{1F3A4}",
|
|
1168
|
-
manSShoe: "\u{1F45E}",
|
|
1169
|
-
manStanding: "\u{1F9CD}\u200D\u2642\uFE0F",
|
|
1170
|
-
manStudent: "\u{1F468}\u200D\u{1F393}",
|
|
1171
|
-
manSuperhero: "\u{1F9B8}\u200D\u2642\uFE0F",
|
|
1172
|
-
manSupervillain: "\u{1F9B9}\u200D\u2642\uFE0F",
|
|
1173
|
-
manSurfing: "\u{1F3C4}\u200D\u2642\uFE0F",
|
|
1174
|
-
manSwimming: "\u{1F3CA}\u200D\u2642\uFE0F",
|
|
1175
|
-
manTeacher: "\u{1F468}\u200D\u{1F3EB}",
|
|
1176
|
-
manTechnologist: "\u{1F468}\u200D\u{1F4BB}",
|
|
1177
|
-
mantelpieceClock: "\u{1F570}\uFE0F",
|
|
1178
|
-
manTippingHand: "\u{1F481}\u200D\u2642\uFE0F",
|
|
1179
|
-
manualWheelchair: "\u{1F9BD}",
|
|
1180
|
-
manVampire: "\u{1F9DB}\u200D\u2642\uFE0F",
|
|
1181
|
-
manWalking: "\u{1F6B6}\u200D\u2642\uFE0F",
|
|
1182
|
-
manWalkingFacingRight: "\u{1F6B6}\u200D\u2642\uFE0F\u200D\u27A1\uFE0F",
|
|
1183
|
-
manWearingTurban: "\u{1F473}\u200D\u2642\uFE0F",
|
|
1184
|
-
manWhiteHair: "\u{1F468}\u200D\u{1F9B3}",
|
|
1185
|
-
manWithVeil: "\u{1F470}\u200D\u2642\uFE0F",
|
|
1186
|
-
manWithWhiteCane: "\u{1F468}\u200D\u{1F9AF}",
|
|
1187
|
-
manWithWhiteCaneFacingRight: "\u{1F468}\u200D\u{1F9AF}\u200D\u27A1\uFE0F",
|
|
1188
|
-
manZombie: "\u{1F9DF}\u200D\u2642\uFE0F",
|
|
1189
|
-
mapleLeaf: "\u{1F341}",
|
|
1190
|
-
mapOfJapan: "\u{1F5FE}",
|
|
1191
|
-
maracas: "\u{1FA87}",
|
|
1192
|
-
martialArtsUniform: "\u{1F94B}",
|
|
1193
|
-
mate: "\u{1F9C9}",
|
|
1194
|
-
meatOnBone: "\u{1F356}",
|
|
1195
|
-
mechanic: "\u{1F9D1}\u200D\u{1F527}",
|
|
1196
|
-
mechanicalArm: "\u{1F9BE}",
|
|
1197
|
-
mechanicalLeg: "\u{1F9BF}",
|
|
1198
|
-
medicalSymbol: "\u2695\uFE0F",
|
|
1199
|
-
megaphone: "\u{1F4E3}",
|
|
1200
|
-
melon: "\u{1F348}",
|
|
1201
|
-
meltingFace: "\u{1FAE0}",
|
|
1202
|
-
memo: "\u{1F4DD}",
|
|
1203
|
-
mendingHeart: "\u2764\uFE0F\u200D\u{1FA79}",
|
|
1204
|
-
menHoldingHands: "\u{1F46C}",
|
|
1205
|
-
menorah: "\u{1F54E}",
|
|
1206
|
-
menSRoom: "\u{1F6B9}",
|
|
1207
|
-
menWithBunnyEars: "\u{1F46F}\u200D\u2642\uFE0F",
|
|
1208
|
-
menWrestling: "\u{1F93C}\u200D\u2642\uFE0F",
|
|
1209
|
-
mermaid: "\u{1F9DC}\u200D\u2640\uFE0F",
|
|
1210
|
-
merman: "\u{1F9DC}\u200D\u2642\uFE0F",
|
|
1211
|
-
merperson: "\u{1F9DC}",
|
|
1212
|
-
metro: "\u{1F687}",
|
|
1213
|
-
microbe: "\u{1F9A0}",
|
|
1214
|
-
microphone: "\u{1F3A4}",
|
|
1215
|
-
microscope: "\u{1F52C}",
|
|
1216
|
-
middleFinger: "\u{1F595}",
|
|
1217
|
-
militaryHelmet: "\u{1FA96}",
|
|
1218
|
-
militaryMedal: "\u{1F396}\uFE0F",
|
|
1219
|
-
milkyWay: "\u{1F30C}",
|
|
1220
|
-
minibus: "\u{1F690}",
|
|
1221
|
-
minus: "\u2796",
|
|
1222
|
-
mirror: "\u{1FA9E}",
|
|
1223
|
-
mirrorBall: "\u{1FAA9}",
|
|
1224
|
-
moai: "\u{1F5FF}",
|
|
1225
|
-
mobilePhone: "\u{1F4F1}",
|
|
1226
|
-
mobilePhoneOff: "\u{1F4F4}",
|
|
1227
|
-
mobilePhoneWithArrow: "\u{1F4F2}",
|
|
1228
|
-
moneyBag: "\u{1F4B0}",
|
|
1229
|
-
moneyMouthFace: "\u{1F911}",
|
|
1230
|
-
moneyWithWings: "\u{1F4B8}",
|
|
1231
|
-
monkey: "\u{1F412}",
|
|
1232
|
-
monkeyFace: "\u{1F435}",
|
|
1233
|
-
monorail: "\u{1F69D}",
|
|
1234
|
-
moonCake: "\u{1F96E}",
|
|
1235
|
-
moonViewingCeremony: "\u{1F391}",
|
|
1236
|
-
moose: "\u{1FACE}",
|
|
1237
|
-
mosque: "\u{1F54C}",
|
|
1238
|
-
mosquito: "\u{1F99F}",
|
|
1239
|
-
motorBoat: "\u{1F6E5}\uFE0F",
|
|
1240
|
-
motorcycle: "\u{1F3CD}\uFE0F",
|
|
1241
|
-
motorizedWheelchair: "\u{1F9BC}",
|
|
1242
|
-
motorScooter: "\u{1F6F5}",
|
|
1243
|
-
motorway: "\u{1F6E3}\uFE0F",
|
|
1244
|
-
mountain: "\u26F0\uFE0F",
|
|
1245
|
-
mountainCableway: "\u{1F6A0}",
|
|
1246
|
-
mountainRailway: "\u{1F69E}",
|
|
1247
|
-
mountFuji: "\u{1F5FB}",
|
|
1248
|
-
mouse: "\u{1F401}",
|
|
1249
|
-
mouseFace: "\u{1F42D}",
|
|
1250
|
-
mouseTrap: "\u{1FAA4}",
|
|
1251
|
-
mouth: "\u{1F444}",
|
|
1252
|
-
movieCamera: "\u{1F3A5}",
|
|
1253
|
-
mrsClaus: "\u{1F936}",
|
|
1254
|
-
multiply: "\u2716\uFE0F",
|
|
1255
|
-
mushroom: "\u{1F344}",
|
|
1256
|
-
musicalKeyboard: "\u{1F3B9}",
|
|
1257
|
-
musicalNote: "\u{1F3B5}",
|
|
1258
|
-
musicalNotes: "\u{1F3B6}",
|
|
1259
|
-
musicalScore: "\u{1F3BC}",
|
|
1260
|
-
mutedSpeaker: "\u{1F507}",
|
|
1261
|
-
mxClaus: "\u{1F9D1}\u200D\u{1F384}",
|
|
1262
|
-
nailPolish: "\u{1F485}",
|
|
1263
|
-
nameBadge: "\u{1F4DB}",
|
|
1264
|
-
nationalPark: "\u{1F3DE}\uFE0F",
|
|
1265
|
-
nauseatedFace: "\u{1F922}",
|
|
1266
|
-
nazarAmulet: "\u{1F9FF}",
|
|
1267
|
-
necktie: "\u{1F454}",
|
|
1268
|
-
nerdFace: "\u{1F913}",
|
|
1269
|
-
nestingDolls: "\u{1FA86}",
|
|
1270
|
-
nestWithEggs: "\u{1FABA}",
|
|
1271
|
-
neutralFace: "\u{1F610}",
|
|
1272
|
-
newButton: "\u{1F195}",
|
|
1273
|
-
newMoon: "\u{1F311}",
|
|
1274
|
-
newMoonFace: "\u{1F31A}",
|
|
1275
|
-
newspaper: "\u{1F4F0}",
|
|
1276
|
-
nextTrackButton: "\u23ED\uFE0F",
|
|
1277
|
-
ngButton: "\u{1F196}",
|
|
1278
|
-
nightWithStars: "\u{1F303}",
|
|
1279
|
-
nineOClock: "\u{1F558}",
|
|
1280
|
-
nineThirty: "\u{1F564}",
|
|
1281
|
-
ninja: "\u{1F977}",
|
|
1282
|
-
noBicycles: "\u{1F6B3}",
|
|
1283
|
-
noEntry: "\u26D4",
|
|
1284
|
-
noLittering: "\u{1F6AF}",
|
|
1285
|
-
noMobilePhones: "\u{1F4F5}",
|
|
1286
|
-
nonPotableWater: "\u{1F6B1}",
|
|
1287
|
-
noOneUnderEighteen: "\u{1F51E}",
|
|
1288
|
-
noPedestrians: "\u{1F6B7}",
|
|
1289
|
-
nose: "\u{1F443}",
|
|
1290
|
-
noSmoking: "\u{1F6AD}",
|
|
1291
|
-
notebook: "\u{1F4D3}",
|
|
1292
|
-
notebookWithDecorativeCover: "\u{1F4D4}",
|
|
1293
|
-
nutAndBolt: "\u{1F529}",
|
|
1294
|
-
oButton: "\u{1F17E}\uFE0F",
|
|
1295
|
-
octopus: "\u{1F419}",
|
|
1296
|
-
oden: "\u{1F362}",
|
|
1297
|
-
officeBuilding: "\u{1F3E2}",
|
|
1298
|
-
officeWorker: "\u{1F9D1}\u200D\u{1F4BC}",
|
|
1299
|
-
ogre: "\u{1F479}",
|
|
1300
|
-
oilDrum: "\u{1F6E2}\uFE0F",
|
|
1301
|
-
okButton: "\u{1F197}",
|
|
1302
|
-
okHand: "\u{1F44C}",
|
|
1303
|
-
olderPerson: "\u{1F9D3}",
|
|
1304
|
-
oldKey: "\u{1F5DD}\uFE0F",
|
|
1305
|
-
oldMan: "\u{1F474}",
|
|
1306
|
-
oldWoman: "\u{1F475}",
|
|
1307
|
-
olive: "\u{1FAD2}",
|
|
1308
|
-
om: "\u{1F549}\uFE0F",
|
|
1309
|
-
onArrow: "\u{1F51B}",
|
|
1310
|
-
oncomingAutomobile: "\u{1F698}",
|
|
1311
|
-
oncomingBus: "\u{1F68D}",
|
|
1312
|
-
oncomingFist: "\u{1F44A}",
|
|
1313
|
-
oncomingPoliceCar: "\u{1F694}",
|
|
1314
|
-
oncomingTaxi: "\u{1F696}",
|
|
1315
|
-
oneOClock: "\u{1F550}",
|
|
1316
|
-
onePieceSwimsuit: "\u{1FA71}",
|
|
1317
|
-
oneThirty: "\u{1F55C}",
|
|
1318
|
-
onion: "\u{1F9C5}",
|
|
1319
|
-
openBook: "\u{1F4D6}",
|
|
1320
|
-
openFileFolder: "\u{1F4C2}",
|
|
1321
|
-
openHands: "\u{1F450}",
|
|
1322
|
-
openMailboxWithLoweredFlag: "\u{1F4ED}",
|
|
1323
|
-
openMailboxWithRaisedFlag: "\u{1F4EC}",
|
|
1324
|
-
ophiuchus: "\u26CE",
|
|
1325
|
-
opticalDisk: "\u{1F4BF}",
|
|
1326
|
-
orangeBook: "\u{1F4D9}",
|
|
1327
|
-
orangeCircle: "\u{1F7E0}",
|
|
1328
|
-
orangeHeart: "\u{1F9E1}",
|
|
1329
|
-
orangeSquare: "\u{1F7E7}",
|
|
1330
|
-
orangutan: "\u{1F9A7}",
|
|
1331
|
-
orca: "\u{1FACD}",
|
|
1332
|
-
orthodoxCross: "\u2626\uFE0F",
|
|
1333
|
-
otter: "\u{1F9A6}",
|
|
1334
|
-
outboxTray: "\u{1F4E4}",
|
|
1335
|
-
owl: "\u{1F989}",
|
|
1336
|
-
ox: "\u{1F402}",
|
|
1337
|
-
oyster: "\u{1F9AA}",
|
|
1338
|
-
package_: "\u{1F4E6}",
|
|
1339
|
-
pageFacingUp: "\u{1F4C4}",
|
|
1340
|
-
pager: "\u{1F4DF}",
|
|
1341
|
-
pageWithCurl: "\u{1F4C3}",
|
|
1342
|
-
paintbrush: "\u{1F58C}\uFE0F",
|
|
1343
|
-
palmDownHand: "\u{1FAF3}",
|
|
1344
|
-
palmsUpTogether: "\u{1F932}",
|
|
1345
|
-
palmTree: "\u{1F334}",
|
|
1346
|
-
palmUpHand: "\u{1FAF4}",
|
|
1347
|
-
pancakes: "\u{1F95E}",
|
|
1348
|
-
panda: "\u{1F43C}",
|
|
1349
|
-
paperclip: "\u{1F4CE}",
|
|
1350
|
-
parachute: "\u{1FA82}",
|
|
1351
|
-
parrot: "\u{1F99C}",
|
|
1352
|
-
partAlternationMark: "\u303D\uFE0F",
|
|
1353
|
-
partyingFace: "\u{1F973}",
|
|
1354
|
-
partyPopper: "\u{1F389}",
|
|
1355
|
-
passengerShip: "\u{1F6F3}\uFE0F",
|
|
1356
|
-
passportControl: "\u{1F6C2}",
|
|
1357
|
-
pauseButton: "\u23F8\uFE0F",
|
|
1358
|
-
pawPrints: "\u{1F43E}",
|
|
1359
|
-
pButton: "\u{1F17F}\uFE0F",
|
|
1360
|
-
peaceSymbol: "\u262E\uFE0F",
|
|
1361
|
-
peach: "\u{1F351}",
|
|
1362
|
-
peacock: "\u{1F99A}",
|
|
1363
|
-
peanuts: "\u{1F95C}",
|
|
1364
|
-
peaPod: "\u{1FADB}",
|
|
1365
|
-
pear: "\u{1F350}",
|
|
1366
|
-
pen: "\u{1F58A}\uFE0F",
|
|
1367
|
-
pencil: "\u270F\uFE0F",
|
|
1368
|
-
penguin: "\u{1F427}",
|
|
1369
|
-
pensiveFace: "\u{1F614}",
|
|
1370
|
-
peopleHoldingHands: "\u{1F9D1}\u200D\u{1F91D}\u200D\u{1F9D1}",
|
|
1371
|
-
peopleHugging: "\u{1FAC2}",
|
|
1372
|
-
peopleWithBunnyEars: "\u{1F46F}",
|
|
1373
|
-
peopleWrestling: "\u{1F93C}",
|
|
1374
|
-
performingArts: "\u{1F3AD}",
|
|
1375
|
-
perseveringFace: "\u{1F623}",
|
|
1376
|
-
person: "\u{1F9D1}",
|
|
1377
|
-
personBald: "\u{1F9D1}\u200D\u{1F9B2}",
|
|
1378
|
-
personBeard: "\u{1F9D4}",
|
|
1379
|
-
personBiking: "\u{1F6B4}",
|
|
1380
|
-
personBlondHair: "\u{1F471}",
|
|
1381
|
-
personBouncingBall: "\u26F9\uFE0F",
|
|
1382
|
-
personBowing: "\u{1F647}",
|
|
1383
|
-
personCartwheeling: "\u{1F938}",
|
|
1384
|
-
personClimbing: "\u{1F9D7}",
|
|
1385
|
-
personCurlyHair: "\u{1F9D1}\u200D\u{1F9B1}",
|
|
1386
|
-
personFacepalming: "\u{1F926}",
|
|
1387
|
-
personFeedingBaby: "\u{1F9D1}\u200D\u{1F37C}",
|
|
1388
|
-
personFencing: "\u{1F93A}",
|
|
1389
|
-
personFrowning: "\u{1F64D}",
|
|
1390
|
-
personGesturingNo: "\u{1F645}",
|
|
1391
|
-
personGesturingOk: "\u{1F646}",
|
|
1392
|
-
personGettingHaircut: "\u{1F487}",
|
|
1393
|
-
personGettingMassage: "\u{1F486}",
|
|
1394
|
-
personGolfing: "\u{1F3CC}\uFE0F",
|
|
1395
|
-
personInBed: "\u{1F6CC}",
|
|
1396
|
-
personInLotusPosition: "\u{1F9D8}",
|
|
1397
|
-
personInManualWheelchair: "\u{1F9D1}\u200D\u{1F9BD}",
|
|
1398
|
-
personInManualWheelchairFacingRight: "\u{1F9D1}\u200D\u{1F9BD}\u200D\u27A1\uFE0F",
|
|
1399
|
-
personInMotorizedWheelchair: "\u{1F9D1}\u200D\u{1F9BC}",
|
|
1400
|
-
personInMotorizedWheelchairFacingRight: "\u{1F9D1}\u200D\u{1F9BC}\u200D\u27A1\uFE0F",
|
|
1401
|
-
personInSteamyRoom: "\u{1F9D6}",
|
|
1402
|
-
personInSuitLevitating: "\u{1F574}\uFE0F",
|
|
1403
|
-
personInTuxedo: "\u{1F935}",
|
|
1404
|
-
personJuggling: "\u{1F939}",
|
|
1405
|
-
personKneeling: "\u{1F9CE}",
|
|
1406
|
-
personKneelingFacingRight: "\u{1F9CE}\u200D\u27A1\uFE0F",
|
|
1407
|
-
personLiftingWeights: "\u{1F3CB}\uFE0F",
|
|
1408
|
-
personMountainBiking: "\u{1F6B5}",
|
|
1409
|
-
personPlayingHandball: "\u{1F93E}",
|
|
1410
|
-
personPlayingWaterPolo: "\u{1F93D}",
|
|
1411
|
-
personPouting: "\u{1F64E}",
|
|
1412
|
-
personRaisingHand: "\u{1F64B}",
|
|
1413
|
-
personRedHair: "\u{1F9D1}\u200D\u{1F9B0}",
|
|
1414
|
-
personRowingBoat: "\u{1F6A3}",
|
|
1415
|
-
personRunning: "\u{1F3C3}",
|
|
1416
|
-
personRunningFacingRight: "\u{1F3C3}\u200D\u27A1\uFE0F",
|
|
1417
|
-
personShrugging: "\u{1F937}",
|
|
1418
|
-
personStanding: "\u{1F9CD}",
|
|
1419
|
-
personSurfing: "\u{1F3C4}",
|
|
1420
|
-
personSwimming: "\u{1F3CA}",
|
|
1421
|
-
personTakingBath: "\u{1F6C0}",
|
|
1422
|
-
personTippingHand: "\u{1F481}",
|
|
1423
|
-
personWalking: "\u{1F6B6}",
|
|
1424
|
-
personWalkingFacingRight: "\u{1F6B6}\u200D\u27A1\uFE0F",
|
|
1425
|
-
personWearingTurban: "\u{1F473}",
|
|
1426
|
-
personWhiteHair: "\u{1F9D1}\u200D\u{1F9B3}",
|
|
1427
|
-
personWithCrown: "\u{1FAC5}",
|
|
1428
|
-
personWithSkullcap: "\u{1F472}",
|
|
1429
|
-
personWithVeil: "\u{1F470}",
|
|
1430
|
-
personWithWhiteCane: "\u{1F9D1}\u200D\u{1F9AF}",
|
|
1431
|
-
personWithWhiteCaneFacingRight: "\u{1F9D1}\u200D\u{1F9AF}\u200D\u27A1\uFE0F",
|
|
1432
|
-
petriDish: "\u{1F9EB}",
|
|
1433
|
-
phoenix: "\u{1F426}\u200D\u{1F525}",
|
|
1434
|
-
pick: "\u26CF\uFE0F",
|
|
1435
|
-
pickupTruck: "\u{1F6FB}",
|
|
1436
|
-
pie: "\u{1F967}",
|
|
1437
|
-
pig: "\u{1F416}",
|
|
1438
|
-
pigFace: "\u{1F437}",
|
|
1439
|
-
pigNose: "\u{1F43D}",
|
|
1440
|
-
pileOfPoo: "\u{1F4A9}",
|
|
1441
|
-
pill: "\u{1F48A}",
|
|
1442
|
-
pilot: "\u{1F9D1}\u200D\u2708\uFE0F",
|
|
1443
|
-
pinata: "\u{1FA85}",
|
|
1444
|
-
pinchedFingers: "\u{1F90C}",
|
|
1445
|
-
pinchingHand: "\u{1F90F}",
|
|
1446
|
-
pineapple: "\u{1F34D}",
|
|
1447
|
-
pineDecoration: "\u{1F38D}",
|
|
1448
|
-
pingPong: "\u{1F3D3}",
|
|
1449
|
-
pinkHeart: "\u{1FA77}",
|
|
1450
|
-
pirateFlag: "\u{1F3F4}\u200D\u2620\uFE0F",
|
|
1451
|
-
pisces: "\u2653",
|
|
1452
|
-
pizza: "\u{1F355}",
|
|
1453
|
-
placard: "\u{1FAA7}",
|
|
1454
|
-
placeOfWorship: "\u{1F6D0}",
|
|
1455
|
-
playButton: "\u25B6\uFE0F",
|
|
1456
|
-
playgroundSlide: "\u{1F6DD}",
|
|
1457
|
-
playOrPauseButton: "\u23EF\uFE0F",
|
|
1458
|
-
pleadingFace: "\u{1F97A}",
|
|
1459
|
-
plunger: "\u{1FAA0}",
|
|
1460
|
-
plus: "\u2795",
|
|
1461
|
-
polarBear: "\u{1F43B}\u200D\u2744\uFE0F",
|
|
1462
|
-
policeCar: "\u{1F693}",
|
|
1463
|
-
policeCarLight: "\u{1F6A8}",
|
|
1464
|
-
policeOfficer: "\u{1F46E}",
|
|
1465
|
-
poodle: "\u{1F429}",
|
|
1466
|
-
pool8Ball: "\u{1F3B1}",
|
|
1467
|
-
popcorn: "\u{1F37F}",
|
|
1468
|
-
postalHorn: "\u{1F4EF}",
|
|
1469
|
-
postbox: "\u{1F4EE}",
|
|
1470
|
-
postOffice: "\u{1F3E4}",
|
|
1471
|
-
potableWater: "\u{1F6B0}",
|
|
1472
|
-
potato: "\u{1F954}",
|
|
1473
|
-
potOfFood: "\u{1F372}",
|
|
1474
|
-
pottedPlant: "\u{1FAB4}",
|
|
1475
|
-
poultryLeg: "\u{1F357}",
|
|
1476
|
-
poundBanknote: "\u{1F4B7}",
|
|
1477
|
-
pouringLiquid: "\u{1FAD7}",
|
|
1478
|
-
poutingCat: "\u{1F63E}",
|
|
1479
|
-
prayerBeads: "\u{1F4FF}",
|
|
1480
|
-
pregnantMan: "\u{1FAC3}",
|
|
1481
|
-
pregnantPerson: "\u{1FAC4}",
|
|
1482
|
-
pregnantWoman: "\u{1F930}",
|
|
1483
|
-
pretzel: "\u{1F968}",
|
|
1484
|
-
prince: "\u{1F934}",
|
|
1485
|
-
princess: "\u{1F478}",
|
|
1486
|
-
printer: "\u{1F5A8}\uFE0F",
|
|
1487
|
-
prohibited: "\u{1F6AB}",
|
|
1488
|
-
purpleCircle: "\u{1F7E3}",
|
|
1489
|
-
purpleHeart: "\u{1F49C}",
|
|
1490
|
-
purpleSquare: "\u{1F7EA}",
|
|
1491
|
-
purse: "\u{1F45B}",
|
|
1492
|
-
pushpin: "\u{1F4CC}",
|
|
1493
|
-
puzzlePiece: "\u{1F9E9}",
|
|
1494
|
-
rabbit: "\u{1F407}",
|
|
1495
|
-
rabbitFace: "\u{1F430}",
|
|
1496
|
-
raccoon: "\u{1F99D}",
|
|
1497
|
-
racingCar: "\u{1F3CE}\uFE0F",
|
|
1498
|
-
radio: "\u{1F4FB}",
|
|
1499
|
-
radioactive: "\u2622\uFE0F",
|
|
1500
|
-
radioButton: "\u{1F518}",
|
|
1501
|
-
railwayCar: "\u{1F683}",
|
|
1502
|
-
railwayTrack: "\u{1F6E4}\uFE0F",
|
|
1503
|
-
rainbow: "\u{1F308}",
|
|
1504
|
-
rainbowFlag: "\u{1F3F3}\uFE0F\u200D\u{1F308}",
|
|
1505
|
-
raisedBackOfHand: "\u{1F91A}",
|
|
1506
|
-
raisedFist: "\u270A",
|
|
1507
|
-
raisedHand: "\u270B",
|
|
1508
|
-
raisingHands: "\u{1F64C}",
|
|
1509
|
-
ram: "\u{1F40F}",
|
|
1510
|
-
rat: "\u{1F400}",
|
|
1511
|
-
razor: "\u{1FA92}",
|
|
1512
|
-
receipt: "\u{1F9FE}",
|
|
1513
|
-
recordButton: "\u23FA\uFE0F",
|
|
1514
|
-
recyclingSymbol: "\u267B\uFE0F",
|
|
1515
|
-
redApple: "\u{1F34E}",
|
|
1516
|
-
redCircle: "\u{1F534}",
|
|
1517
|
-
redEnvelope: "\u{1F9E7}",
|
|
1518
|
-
redExclamationMark: "\u2757",
|
|
1519
|
-
redHeart: "\u2764\uFE0F",
|
|
1520
|
-
redPaperLantern: "\u{1F3EE}",
|
|
1521
|
-
redQuestionMark: "\u2753",
|
|
1522
|
-
redSquare: "\u{1F7E5}",
|
|
1523
|
-
redTrianglePointedDown: "\u{1F53B}",
|
|
1524
|
-
redTrianglePointedUp: "\u{1F53A}",
|
|
1525
|
-
registered: "\xAE\uFE0F",
|
|
1526
|
-
relievedFace: "\u{1F60C}",
|
|
1527
|
-
reminderRibbon: "\u{1F397}\uFE0F",
|
|
1528
|
-
repeatButton: "\u{1F501}",
|
|
1529
|
-
repeatSingleButton: "\u{1F502}",
|
|
1530
|
-
rescueWorkerSHelmet: "\u26D1\uFE0F",
|
|
1531
|
-
restroom: "\u{1F6BB}",
|
|
1532
|
-
reverseButton: "\u25C0\uFE0F",
|
|
1533
|
-
revolvingHearts: "\u{1F49E}",
|
|
1534
|
-
rhinoceros: "\u{1F98F}",
|
|
1535
|
-
ribbon: "\u{1F380}",
|
|
1536
|
-
riceBall: "\u{1F359}",
|
|
1537
|
-
riceCracker: "\u{1F358}",
|
|
1538
|
-
rightAngerBubble: "\u{1F5EF}\uFE0F",
|
|
1539
|
-
rightArrow: "\u27A1\uFE0F",
|
|
1540
|
-
rightArrowCurvingDown: "\u2935\uFE0F",
|
|
1541
|
-
rightArrowCurvingLeft: "\u21A9\uFE0F",
|
|
1542
|
-
rightArrowCurvingUp: "\u2934\uFE0F",
|
|
1543
|
-
rightFacingFist: "\u{1F91C}",
|
|
1544
|
-
rightwardsHand: "\u{1FAF1}",
|
|
1545
|
-
rightwardsPushingHand: "\u{1FAF8}",
|
|
1546
|
-
ring: "\u{1F48D}",
|
|
1547
|
-
ringBuoy: "\u{1F6DF}",
|
|
1548
|
-
ringedPlanet: "\u{1FA90}",
|
|
1549
|
-
roastedSweetPotato: "\u{1F360}",
|
|
1550
|
-
robot: "\u{1F916}",
|
|
1551
|
-
rock: "\u{1FAA8}",
|
|
1552
|
-
rocket: "\u{1F680}",
|
|
1553
|
-
rolledUpNewspaper: "\u{1F5DE}\uFE0F",
|
|
1554
|
-
rollerCoaster: "\u{1F3A2}",
|
|
1555
|
-
rollerSkate: "\u{1F6FC}",
|
|
1556
|
-
rollingOnTheFloorLaughing: "\u{1F923}",
|
|
1557
|
-
rollOfPaper: "\u{1F9FB}",
|
|
1558
|
-
rooster: "\u{1F413}",
|
|
1559
|
-
rootVegetable: "\u{1FADC}",
|
|
1560
|
-
rose: "\u{1F339}",
|
|
1561
|
-
rosette: "\u{1F3F5}\uFE0F",
|
|
1562
|
-
roundPushpin: "\u{1F4CD}",
|
|
1563
|
-
rugbyFootball: "\u{1F3C9}",
|
|
1564
|
-
runningShirt: "\u{1F3BD}",
|
|
1565
|
-
runningShoe: "\u{1F45F}",
|
|
1566
|
-
sadButRelievedFace: "\u{1F625}",
|
|
1567
|
-
safetyPin: "\u{1F9F7}",
|
|
1568
|
-
safetyVest: "\u{1F9BA}",
|
|
1569
|
-
sagittarius: "\u2650",
|
|
1570
|
-
sailboat: "\u26F5",
|
|
1571
|
-
sake: "\u{1F376}",
|
|
1572
|
-
salt: "\u{1F9C2}",
|
|
1573
|
-
salutingFace: "\u{1FAE1}",
|
|
1574
|
-
sandwich: "\u{1F96A}",
|
|
1575
|
-
santaClaus: "\u{1F385}",
|
|
1576
|
-
sari: "\u{1F97B}",
|
|
1577
|
-
satellite: "\u{1F6F0}\uFE0F",
|
|
1578
|
-
satelliteAntenna: "\u{1F4E1}",
|
|
1579
|
-
sauropod: "\u{1F995}",
|
|
1580
|
-
saxophone: "\u{1F3B7}",
|
|
1581
|
-
scarf: "\u{1F9E3}",
|
|
1582
|
-
school: "\u{1F3EB}",
|
|
1583
|
-
scientist: "\u{1F9D1}\u200D\u{1F52C}",
|
|
1584
|
-
scissors: "\u2702\uFE0F",
|
|
1585
|
-
scorpio: "\u264F",
|
|
1586
|
-
scorpion: "\u{1F982}",
|
|
1587
|
-
screwdriver: "\u{1FA9B}",
|
|
1588
|
-
scroll: "\u{1F4DC}",
|
|
1589
|
-
seal: "\u{1F9AD}",
|
|
1590
|
-
seat: "\u{1F4BA}",
|
|
1591
|
-
seedling: "\u{1F331}",
|
|
1592
|
-
seeNoEvilMonkey: "\u{1F648}",
|
|
1593
|
-
selfie: "\u{1F933}",
|
|
1594
|
-
serviceDog: "\u{1F415}\u200D\u{1F9BA}",
|
|
1595
|
-
sevenOClock: "\u{1F556}",
|
|
1596
|
-
sevenThirty: "\u{1F562}",
|
|
1597
|
-
sewingNeedle: "\u{1FAA1}",
|
|
1598
|
-
shakingFace: "\u{1FAE8}",
|
|
1599
|
-
shallowPanOfFood: "\u{1F958}",
|
|
1600
|
-
shamrock: "\u2618\uFE0F",
|
|
1601
|
-
shark: "\u{1F988}",
|
|
1602
|
-
shavedIce: "\u{1F367}",
|
|
1603
|
-
sheafOfRice: "\u{1F33E}",
|
|
1604
|
-
shield: "\u{1F6E1}\uFE0F",
|
|
1605
|
-
shintoShrine: "\u26E9\uFE0F",
|
|
1606
|
-
ship: "\u{1F6A2}",
|
|
1607
|
-
shootingStar: "\u{1F320}",
|
|
1608
|
-
shoppingBags: "\u{1F6CD}\uFE0F",
|
|
1609
|
-
shoppingCart: "\u{1F6D2}",
|
|
1610
|
-
shortcake: "\u{1F370}",
|
|
1611
|
-
shorts: "\u{1FA73}",
|
|
1612
|
-
shovel: "\u{1FA8F}",
|
|
1613
|
-
shower: "\u{1F6BF}",
|
|
1614
|
-
shrimp: "\u{1F990}",
|
|
1615
|
-
shuffleTracksButton: "\u{1F500}",
|
|
1616
|
-
shushingFace: "\u{1F92B}",
|
|
1617
|
-
signOfTheHorns: "\u{1F918}",
|
|
1618
|
-
singer: "\u{1F9D1}\u200D\u{1F3A4}",
|
|
1619
|
-
sixOClock: "\u{1F555}",
|
|
1620
|
-
sixThirty: "\u{1F561}",
|
|
1621
|
-
skateboard: "\u{1F6F9}",
|
|
1622
|
-
skier: "\u26F7\uFE0F",
|
|
1623
|
-
skis: "\u{1F3BF}",
|
|
1624
|
-
skull: "\u{1F480}",
|
|
1625
|
-
skullAndCrossbones: "\u2620\uFE0F",
|
|
1626
|
-
skunk: "\u{1F9A8}",
|
|
1627
|
-
sled: "\u{1F6F7}",
|
|
1628
|
-
sleepingFace: "\u{1F634}",
|
|
1629
|
-
sleepyFace: "\u{1F62A}",
|
|
1630
|
-
slightlyFrowningFace: "\u{1F641}",
|
|
1631
|
-
slightlySmilingFace: "\u{1F642}",
|
|
1632
|
-
sloth: "\u{1F9A5}",
|
|
1633
|
-
slotMachine: "\u{1F3B0}",
|
|
1634
|
-
smallAirplane: "\u{1F6E9}\uFE0F",
|
|
1635
|
-
smallBlueDiamond: "\u{1F539}",
|
|
1636
|
-
smallOrangeDiamond: "\u{1F538}",
|
|
1637
|
-
smilingCatWithHeartEyes: "\u{1F63B}",
|
|
1638
|
-
smilingFace: "\u263A\uFE0F",
|
|
1639
|
-
smilingFaceWithHalo: "\u{1F607}",
|
|
1640
|
-
smilingFaceWithHeartEyes: "\u{1F60D}",
|
|
1641
|
-
smilingFaceWithHearts: "\u{1F970}",
|
|
1642
|
-
smilingFaceWithHorns: "\u{1F608}",
|
|
1643
|
-
smilingFaceWithOpenHands: "\u{1F917}",
|
|
1644
|
-
smilingFaceWithSmilingEyes: "\u{1F60A}",
|
|
1645
|
-
smilingFaceWithSunglasses: "\u{1F60E}",
|
|
1646
|
-
smilingFaceWithTear: "\u{1F972}",
|
|
1647
|
-
smirkingFace: "\u{1F60F}",
|
|
1648
|
-
snail: "\u{1F40C}",
|
|
1649
|
-
snake: "\u{1F40D}",
|
|
1650
|
-
sneezingFace: "\u{1F927}",
|
|
1651
|
-
snowboarder: "\u{1F3C2}",
|
|
1652
|
-
snowCappedMountain: "\u{1F3D4}\uFE0F",
|
|
1653
|
-
snowflake: "\u2744\uFE0F",
|
|
1654
|
-
snowman: "\u2603\uFE0F",
|
|
1655
|
-
snowmanWithoutSnow: "\u26C4",
|
|
1656
|
-
soap: "\u{1F9FC}",
|
|
1657
|
-
soccerBall: "\u26BD",
|
|
1658
|
-
socks: "\u{1F9E6}",
|
|
1659
|
-
softball: "\u{1F94E}",
|
|
1660
|
-
softIceCream: "\u{1F366}",
|
|
1661
|
-
soonArrow: "\u{1F51C}",
|
|
1662
|
-
sosButton: "\u{1F198}",
|
|
1663
|
-
spadeSuit: "\u2660\uFE0F",
|
|
1664
|
-
spaghetti: "\u{1F35D}",
|
|
1665
|
-
sparkle: "\u2747\uFE0F",
|
|
1666
|
-
sparkler: "\u{1F387}",
|
|
1667
|
-
sparkles: "\u2728",
|
|
1668
|
-
sparklingHeart: "\u{1F496}",
|
|
1669
|
-
speakerHighVolume: "\u{1F50A}",
|
|
1670
|
-
speakerLowVolume: "\u{1F508}",
|
|
1671
|
-
speakerMediumVolume: "\u{1F509}",
|
|
1672
|
-
speakingHead: "\u{1F5E3}\uFE0F",
|
|
1673
|
-
speakNoEvilMonkey: "\u{1F64A}",
|
|
1674
|
-
speechBalloon: "\u{1F4AC}",
|
|
1675
|
-
speedboat: "\u{1F6A4}",
|
|
1676
|
-
spider: "\u{1F577}\uFE0F",
|
|
1677
|
-
spiderWeb: "\u{1F578}\uFE0F",
|
|
1678
|
-
spiralCalendar: "\u{1F5D3}\uFE0F",
|
|
1679
|
-
spiralNotepad: "\u{1F5D2}\uFE0F",
|
|
1680
|
-
spiralShell: "\u{1F41A}",
|
|
1681
|
-
splatter: "\u{1FADF}",
|
|
1682
|
-
sponge: "\u{1F9FD}",
|
|
1683
|
-
spoon: "\u{1F944}",
|
|
1684
|
-
sportsMedal: "\u{1F3C5}",
|
|
1685
|
-
sportUtilityVehicle: "\u{1F699}",
|
|
1686
|
-
spoutingWhale: "\u{1F433}",
|
|
1687
|
-
squid: "\u{1F991}",
|
|
1688
|
-
squintingFaceWithTongue: "\u{1F61D}",
|
|
1689
|
-
stadium: "\u{1F3DF}\uFE0F",
|
|
1690
|
-
star: "\u2B50",
|
|
1691
|
-
starAndCrescent: "\u262A\uFE0F",
|
|
1692
|
-
starOfDavid: "\u2721\uFE0F",
|
|
1693
|
-
starStruck: "\u{1F929}",
|
|
1694
|
-
station: "\u{1F689}",
|
|
1695
|
-
statueOfLiberty: "\u{1F5FD}",
|
|
1696
|
-
steamingBowl: "\u{1F35C}",
|
|
1697
|
-
stethoscope: "\u{1FA7A}",
|
|
1698
|
-
stopButton: "\u23F9\uFE0F",
|
|
1699
|
-
stopSign: "\u{1F6D1}",
|
|
1700
|
-
stopwatch: "\u23F1\uFE0F",
|
|
1701
|
-
straightRuler: "\u{1F4CF}",
|
|
1702
|
-
strawberry: "\u{1F353}",
|
|
1703
|
-
student: "\u{1F9D1}\u200D\u{1F393}",
|
|
1704
|
-
studioMicrophone: "\u{1F399}\uFE0F",
|
|
1705
|
-
stuffedFlatbread: "\u{1F959}",
|
|
1706
|
-
sun: "\u2600\uFE0F",
|
|
1707
|
-
sunBehindCloud: "\u26C5",
|
|
1708
|
-
sunBehindLargeCloud: "\u{1F325}\uFE0F",
|
|
1709
|
-
sunBehindRainCloud: "\u{1F326}\uFE0F",
|
|
1710
|
-
sunBehindSmallCloud: "\u{1F324}\uFE0F",
|
|
1711
|
-
sunflower: "\u{1F33B}",
|
|
1712
|
-
sunglasses: "\u{1F576}\uFE0F",
|
|
1713
|
-
sunrise: "\u{1F305}",
|
|
1714
|
-
sunriseOverMountains: "\u{1F304}",
|
|
1715
|
-
sunset: "\u{1F307}",
|
|
1716
|
-
sunWithFace: "\u{1F31E}",
|
|
1717
|
-
superhero: "\u{1F9B8}",
|
|
1718
|
-
supervillain: "\u{1F9B9}",
|
|
1719
|
-
sushi: "\u{1F363}",
|
|
1720
|
-
suspensionRailway: "\u{1F69F}",
|
|
1721
|
-
swan: "\u{1F9A2}",
|
|
1722
|
-
sweatDroplets: "\u{1F4A6}",
|
|
1723
|
-
synagogue: "\u{1F54D}",
|
|
1724
|
-
syringe: "\u{1F489}",
|
|
1725
|
-
taco: "\u{1F32E}",
|
|
1726
|
-
takeoutBox: "\u{1F961}",
|
|
1727
|
-
tamale: "\u{1FAD4}",
|
|
1728
|
-
tanabataTree: "\u{1F38B}",
|
|
1729
|
-
tangerine: "\u{1F34A}",
|
|
1730
|
-
taurus: "\u2649",
|
|
1731
|
-
taxi: "\u{1F695}",
|
|
1732
|
-
teacher: "\u{1F9D1}\u200D\u{1F3EB}",
|
|
1733
|
-
teacupWithoutHandle: "\u{1F375}",
|
|
1734
|
-
teapot: "\u{1FAD6}",
|
|
1735
|
-
tearOffCalendar: "\u{1F4C6}",
|
|
1736
|
-
technologist: "\u{1F9D1}\u200D\u{1F4BB}",
|
|
1737
|
-
teddyBear: "\u{1F9F8}",
|
|
1738
|
-
telephone: "\u260E\uFE0F",
|
|
1739
|
-
telephoneReceiver: "\u{1F4DE}",
|
|
1740
|
-
telescope: "\u{1F52D}",
|
|
1741
|
-
television: "\u{1F4FA}",
|
|
1742
|
-
tennis: "\u{1F3BE}",
|
|
1743
|
-
tenOClock: "\u{1F559}",
|
|
1744
|
-
tent: "\u26FA",
|
|
1745
|
-
tenThirty: "\u{1F565}",
|
|
1746
|
-
testTube: "\u{1F9EA}",
|
|
1747
|
-
thermometer: "\u{1F321}\uFE0F",
|
|
1748
|
-
thinkingFace: "\u{1F914}",
|
|
1749
|
-
thongSandal: "\u{1FA74}",
|
|
1750
|
-
thoughtBalloon: "\u{1F4AD}",
|
|
1751
|
-
thread: "\u{1F9F5}",
|
|
1752
|
-
threeOClock: "\u{1F552}",
|
|
1753
|
-
threeThirty: "\u{1F55E}",
|
|
1754
|
-
thumbsDown: "\u{1F44E}",
|
|
1755
|
-
thumbsUp: "\u{1F44D}",
|
|
1756
|
-
ticket: "\u{1F3AB}",
|
|
1757
|
-
tiger: "\u{1F405}",
|
|
1758
|
-
tigerFace: "\u{1F42F}",
|
|
1759
|
-
timerClock: "\u23F2\uFE0F",
|
|
1760
|
-
tiredFace: "\u{1F62B}",
|
|
1761
|
-
toilet: "\u{1F6BD}",
|
|
1762
|
-
tokyoTower: "\u{1F5FC}",
|
|
1763
|
-
tomato: "\u{1F345}",
|
|
1764
|
-
tongue: "\u{1F445}",
|
|
1765
|
-
toolbox: "\u{1F9F0}",
|
|
1766
|
-
tooth: "\u{1F9B7}",
|
|
1767
|
-
toothbrush: "\u{1FAA5}",
|
|
1768
|
-
topArrow: "\u{1F51D}",
|
|
1769
|
-
topHat: "\u{1F3A9}",
|
|
1770
|
-
tornado: "\u{1F32A}\uFE0F",
|
|
1771
|
-
trackball: "\u{1F5B2}\uFE0F",
|
|
1772
|
-
tractor: "\u{1F69C}",
|
|
1773
|
-
tradeMark: "\u2122\uFE0F",
|
|
1774
|
-
train: "\u{1F686}",
|
|
1775
|
-
tram: "\u{1F68A}",
|
|
1776
|
-
tramCar: "\u{1F68B}",
|
|
1777
|
-
transgenderFlag: "\u{1F3F3}\uFE0F\u200D\u26A7\uFE0F",
|
|
1778
|
-
transgenderSymbol: "\u26A7\uFE0F",
|
|
1779
|
-
treasureChest: "\u{1FA8E}",
|
|
1780
|
-
tRex: "\u{1F996}",
|
|
1781
|
-
triangularFlag: "\u{1F6A9}",
|
|
1782
|
-
triangularRuler: "\u{1F4D0}",
|
|
1783
|
-
tridentEmblem: "\u{1F531}",
|
|
1784
|
-
troll: "\u{1F9CC}",
|
|
1785
|
-
trolleybus: "\u{1F68E}",
|
|
1786
|
-
trombone: "\u{1FA8A}",
|
|
1787
|
-
trophy: "\u{1F3C6}",
|
|
1788
|
-
tropicalDrink: "\u{1F379}",
|
|
1789
|
-
tropicalFish: "\u{1F420}",
|
|
1790
|
-
trumpet: "\u{1F3BA}",
|
|
1791
|
-
tShirt: "\u{1F455}",
|
|
1792
|
-
tulip: "\u{1F337}",
|
|
1793
|
-
tumblerGlass: "\u{1F943}",
|
|
1794
|
-
turkey: "\u{1F983}",
|
|
1795
|
-
turtle: "\u{1F422}",
|
|
1796
|
-
twelveOClock: "\u{1F55B}",
|
|
1797
|
-
twelveThirty: "\u{1F567}",
|
|
1798
|
-
twoHearts: "\u{1F495}",
|
|
1799
|
-
twoHumpCamel: "\u{1F42B}",
|
|
1800
|
-
twoOClock: "\u{1F551}",
|
|
1801
|
-
twoThirty: "\u{1F55D}",
|
|
1802
|
-
umbrella: "\u2602\uFE0F",
|
|
1803
|
-
umbrellaOnGround: "\u26F1\uFE0F",
|
|
1804
|
-
umbrellaWithRainDrops: "\u2614",
|
|
1805
|
-
unamusedFace: "\u{1F612}",
|
|
1806
|
-
unicorn: "\u{1F984}",
|
|
1807
|
-
unlocked: "\u{1F513}",
|
|
1808
|
-
upArrow: "\u2B06\uFE0F",
|
|
1809
|
-
upButton: "\u{1F199}",
|
|
1810
|
-
upDownArrow: "\u2195\uFE0F",
|
|
1811
|
-
upLeftArrow: "\u2196\uFE0F",
|
|
1812
|
-
upRightArrow: "\u2197\uFE0F",
|
|
1813
|
-
upsideDownFace: "\u{1F643}",
|
|
1814
|
-
upwardsButton: "\u{1F53C}",
|
|
1815
|
-
vampire: "\u{1F9DB}",
|
|
1816
|
-
verticalTrafficLight: "\u{1F6A6}",
|
|
1817
|
-
vibrationMode: "\u{1F4F3}",
|
|
1818
|
-
victoryHand: "\u270C\uFE0F",
|
|
1819
|
-
videoCamera: "\u{1F4F9}",
|
|
1820
|
-
videocassette: "\u{1F4FC}",
|
|
1821
|
-
videoGame: "\u{1F3AE}",
|
|
1822
|
-
violin: "\u{1F3BB}",
|
|
1823
|
-
virgo: "\u264D",
|
|
1824
|
-
volcano: "\u{1F30B}",
|
|
1825
|
-
volleyball: "\u{1F3D0}",
|
|
1826
|
-
vsButton: "\u{1F19A}",
|
|
1827
|
-
vulcanSalute: "\u{1F596}",
|
|
1828
|
-
waffle: "\u{1F9C7}",
|
|
1829
|
-
waningCrescentMoon: "\u{1F318}",
|
|
1830
|
-
waningGibbousMoon: "\u{1F316}",
|
|
1831
|
-
warning: "\u26A0\uFE0F",
|
|
1832
|
-
wastebasket: "\u{1F5D1}\uFE0F",
|
|
1833
|
-
watch: "\u231A",
|
|
1834
|
-
waterBuffalo: "\u{1F403}",
|
|
1835
|
-
waterCloset: "\u{1F6BE}",
|
|
1836
|
-
watermelon: "\u{1F349}",
|
|
1837
|
-
waterPistol: "\u{1F52B}",
|
|
1838
|
-
waterWave: "\u{1F30A}",
|
|
1839
|
-
wavingHand: "\u{1F44B}",
|
|
1840
|
-
wavyDash: "\u3030\uFE0F",
|
|
1841
|
-
waxingCrescentMoon: "\u{1F312}",
|
|
1842
|
-
waxingGibbousMoon: "\u{1F314}",
|
|
1843
|
-
wearyCat: "\u{1F640}",
|
|
1844
|
-
wearyFace: "\u{1F629}",
|
|
1845
|
-
wedding: "\u{1F492}",
|
|
1846
|
-
whale: "\u{1F40B}",
|
|
1847
|
-
wheel: "\u{1F6DE}",
|
|
1848
|
-
wheelchairSymbol: "\u267F",
|
|
1849
|
-
wheelOfDharma: "\u2638\uFE0F",
|
|
1850
|
-
whiteCane: "\u{1F9AF}",
|
|
1851
|
-
whiteCircle: "\u26AA",
|
|
1852
|
-
whiteExclamationMark: "\u2755",
|
|
1853
|
-
whiteFlag: "\u{1F3F3}\uFE0F",
|
|
1854
|
-
whiteFlower: "\u{1F4AE}",
|
|
1855
|
-
whiteHeart: "\u{1F90D}",
|
|
1856
|
-
whiteLargeSquare: "\u2B1C",
|
|
1857
|
-
whiteMediumSmallSquare: "\u25FD",
|
|
1858
|
-
whiteMediumSquare: "\u25FB\uFE0F",
|
|
1859
|
-
whiteQuestionMark: "\u2754",
|
|
1860
|
-
whiteSmallSquare: "\u25AB\uFE0F",
|
|
1861
|
-
whiteSquareButton: "\u{1F533}",
|
|
1862
|
-
wiltedFlower: "\u{1F940}",
|
|
1863
|
-
windChime: "\u{1F390}",
|
|
1864
|
-
windFace: "\u{1F32C}\uFE0F",
|
|
1865
|
-
window: "\u{1FA9F}",
|
|
1866
|
-
wineGlass: "\u{1F377}",
|
|
1867
|
-
wing: "\u{1FABD}",
|
|
1868
|
-
winkingFace: "\u{1F609}",
|
|
1869
|
-
winkingFaceWithTongue: "\u{1F61C}",
|
|
1870
|
-
wireless: "\u{1F6DC}",
|
|
1871
|
-
wolf: "\u{1F43A}",
|
|
1872
|
-
woman: "\u{1F469}",
|
|
1873
|
-
womanAndManHoldingHands: "\u{1F46B}",
|
|
1874
|
-
womanArtist: "\u{1F469}\u200D\u{1F3A8}",
|
|
1875
|
-
womanAstronaut: "\u{1F469}\u200D\u{1F680}",
|
|
1876
|
-
womanBald: "\u{1F469}\u200D\u{1F9B2}",
|
|
1877
|
-
womanBeard: "\u{1F9D4}\u200D\u2640\uFE0F",
|
|
1878
|
-
womanBiking: "\u{1F6B4}\u200D\u2640\uFE0F",
|
|
1879
|
-
womanBlondHair: "\u{1F471}\u200D\u2640\uFE0F",
|
|
1880
|
-
womanBouncingBall: "\u26F9\uFE0F\u200D\u2640\uFE0F",
|
|
1881
|
-
womanBowing: "\u{1F647}\u200D\u2640\uFE0F",
|
|
1882
|
-
womanCartwheeling: "\u{1F938}\u200D\u2640\uFE0F",
|
|
1883
|
-
womanClimbing: "\u{1F9D7}\u200D\u2640\uFE0F",
|
|
1884
|
-
womanConstructionWorker: "\u{1F477}\u200D\u2640\uFE0F",
|
|
1885
|
-
womanCook: "\u{1F469}\u200D\u{1F373}",
|
|
1886
|
-
womanCurlyHair: "\u{1F469}\u200D\u{1F9B1}",
|
|
1887
|
-
womanDancing: "\u{1F483}",
|
|
1888
|
-
womanDetective: "\u{1F575}\uFE0F\u200D\u2640\uFE0F",
|
|
1889
|
-
womanElf: "\u{1F9DD}\u200D\u2640\uFE0F",
|
|
1890
|
-
womanFacepalming: "\u{1F926}\u200D\u2640\uFE0F",
|
|
1891
|
-
womanFactoryWorker: "\u{1F469}\u200D\u{1F3ED}",
|
|
1892
|
-
womanFairy: "\u{1F9DA}\u200D\u2640\uFE0F",
|
|
1893
|
-
womanFarmer: "\u{1F469}\u200D\u{1F33E}",
|
|
1894
|
-
womanFeedingBaby: "\u{1F469}\u200D\u{1F37C}",
|
|
1895
|
-
womanFirefighter: "\u{1F469}\u200D\u{1F692}",
|
|
1896
|
-
womanFrowning: "\u{1F64D}\u200D\u2640\uFE0F",
|
|
1897
|
-
womanGenie: "\u{1F9DE}\u200D\u2640\uFE0F",
|
|
1898
|
-
womanGesturingNo: "\u{1F645}\u200D\u2640\uFE0F",
|
|
1899
|
-
womanGesturingOk: "\u{1F646}\u200D\u2640\uFE0F",
|
|
1900
|
-
womanGettingHaircut: "\u{1F487}\u200D\u2640\uFE0F",
|
|
1901
|
-
womanGettingMassage: "\u{1F486}\u200D\u2640\uFE0F",
|
|
1902
|
-
womanGolfing: "\u{1F3CC}\uFE0F\u200D\u2640\uFE0F",
|
|
1903
|
-
womanGuard: "\u{1F482}\u200D\u2640\uFE0F",
|
|
1904
|
-
womanHealthWorker: "\u{1F469}\u200D\u2695\uFE0F",
|
|
1905
|
-
womanInLotusPosition: "\u{1F9D8}\u200D\u2640\uFE0F",
|
|
1906
|
-
womanInManualWheelchair: "\u{1F469}\u200D\u{1F9BD}",
|
|
1907
|
-
womanInManualWheelchairFacingRight: "\u{1F469}\u200D\u{1F9BD}\u200D\u27A1\uFE0F",
|
|
1908
|
-
womanInMotorizedWheelchair: "\u{1F469}\u200D\u{1F9BC}",
|
|
1909
|
-
womanInMotorizedWheelchairFacingRight: "\u{1F469}\u200D\u{1F9BC}\u200D\u27A1\uFE0F",
|
|
1910
|
-
womanInSteamyRoom: "\u{1F9D6}\u200D\u2640\uFE0F",
|
|
1911
|
-
womanInTuxedo: "\u{1F935}\u200D\u2640\uFE0F",
|
|
1912
|
-
womanJudge: "\u{1F469}\u200D\u2696\uFE0F",
|
|
1913
|
-
womanJuggling: "\u{1F939}\u200D\u2640\uFE0F",
|
|
1914
|
-
womanKneeling: "\u{1F9CE}\u200D\u2640\uFE0F",
|
|
1915
|
-
womanKneelingFacingRight: "\u{1F9CE}\u200D\u2640\uFE0F\u200D\u27A1\uFE0F",
|
|
1916
|
-
womanLiftingWeights: "\u{1F3CB}\uFE0F\u200D\u2640\uFE0F",
|
|
1917
|
-
womanMage: "\u{1F9D9}\u200D\u2640\uFE0F",
|
|
1918
|
-
womanMechanic: "\u{1F469}\u200D\u{1F527}",
|
|
1919
|
-
womanMountainBiking: "\u{1F6B5}\u200D\u2640\uFE0F",
|
|
1920
|
-
womanOfficeWorker: "\u{1F469}\u200D\u{1F4BC}",
|
|
1921
|
-
womanPilot: "\u{1F469}\u200D\u2708\uFE0F",
|
|
1922
|
-
womanPlayingHandball: "\u{1F93E}\u200D\u2640\uFE0F",
|
|
1923
|
-
womanPlayingWaterPolo: "\u{1F93D}\u200D\u2640\uFE0F",
|
|
1924
|
-
womanPoliceOfficer: "\u{1F46E}\u200D\u2640\uFE0F",
|
|
1925
|
-
womanPouting: "\u{1F64E}\u200D\u2640\uFE0F",
|
|
1926
|
-
womanRaisingHand: "\u{1F64B}\u200D\u2640\uFE0F",
|
|
1927
|
-
womanRedHair: "\u{1F469}\u200D\u{1F9B0}",
|
|
1928
|
-
womanRowingBoat: "\u{1F6A3}\u200D\u2640\uFE0F",
|
|
1929
|
-
womanRunning: "\u{1F3C3}\u200D\u2640\uFE0F",
|
|
1930
|
-
womanRunningFacingRight: "\u{1F3C3}\u200D\u2640\uFE0F\u200D\u27A1\uFE0F",
|
|
1931
|
-
womanSBoot: "\u{1F462}",
|
|
1932
|
-
womanScientist: "\u{1F469}\u200D\u{1F52C}",
|
|
1933
|
-
womanSClothes: "\u{1F45A}",
|
|
1934
|
-
womanSHat: "\u{1F452}",
|
|
1935
|
-
womanShrugging: "\u{1F937}\u200D\u2640\uFE0F",
|
|
1936
|
-
womanSinger: "\u{1F469}\u200D\u{1F3A4}",
|
|
1937
|
-
womanSSandal: "\u{1F461}",
|
|
1938
|
-
womanStanding: "\u{1F9CD}\u200D\u2640\uFE0F",
|
|
1939
|
-
womanStudent: "\u{1F469}\u200D\u{1F393}",
|
|
1940
|
-
womanSuperhero: "\u{1F9B8}\u200D\u2640\uFE0F",
|
|
1941
|
-
womanSupervillain: "\u{1F9B9}\u200D\u2640\uFE0F",
|
|
1942
|
-
womanSurfing: "\u{1F3C4}\u200D\u2640\uFE0F",
|
|
1943
|
-
womanSwimming: "\u{1F3CA}\u200D\u2640\uFE0F",
|
|
1944
|
-
womanTeacher: "\u{1F469}\u200D\u{1F3EB}",
|
|
1945
|
-
womanTechnologist: "\u{1F469}\u200D\u{1F4BB}",
|
|
1946
|
-
womanTippingHand: "\u{1F481}\u200D\u2640\uFE0F",
|
|
1947
|
-
womanVampire: "\u{1F9DB}\u200D\u2640\uFE0F",
|
|
1948
|
-
womanWalking: "\u{1F6B6}\u200D\u2640\uFE0F",
|
|
1949
|
-
womanWalkingFacingRight: "\u{1F6B6}\u200D\u2640\uFE0F\u200D\u27A1\uFE0F",
|
|
1950
|
-
womanWearingTurban: "\u{1F473}\u200D\u2640\uFE0F",
|
|
1951
|
-
womanWhiteHair: "\u{1F469}\u200D\u{1F9B3}",
|
|
1952
|
-
womanWithHeadscarf: "\u{1F9D5}",
|
|
1953
|
-
womanWithVeil: "\u{1F470}\u200D\u2640\uFE0F",
|
|
1954
|
-
womanWithWhiteCane: "\u{1F469}\u200D\u{1F9AF}",
|
|
1955
|
-
womanWithWhiteCaneFacingRight: "\u{1F469}\u200D\u{1F9AF}\u200D\u27A1\uFE0F",
|
|
1956
|
-
womanZombie: "\u{1F9DF}\u200D\u2640\uFE0F",
|
|
1957
|
-
womenHoldingHands: "\u{1F46D}",
|
|
1958
|
-
womenSRoom: "\u{1F6BA}",
|
|
1959
|
-
womenWithBunnyEars: "\u{1F46F}\u200D\u2640\uFE0F",
|
|
1960
|
-
womenWrestling: "\u{1F93C}\u200D\u2640\uFE0F",
|
|
1961
|
-
wood: "\u{1FAB5}",
|
|
1962
|
-
woozyFace: "\u{1F974}",
|
|
1963
|
-
worldMap: "\u{1F5FA}\uFE0F",
|
|
1964
|
-
worm: "\u{1FAB1}",
|
|
1965
|
-
worriedFace: "\u{1F61F}",
|
|
1966
|
-
wrappedGift: "\u{1F381}",
|
|
1967
|
-
wrench: "\u{1F527}",
|
|
1968
|
-
writingHand: "\u270D\uFE0F",
|
|
1969
|
-
xRay: "\u{1FA7B}",
|
|
1970
|
-
yarn: "\u{1F9F6}",
|
|
1971
|
-
yawningFace: "\u{1F971}",
|
|
1972
|
-
yellowCircle: "\u{1F7E1}",
|
|
1973
|
-
yellowHeart: "\u{1F49B}",
|
|
1974
|
-
yellowSquare: "\u{1F7E8}",
|
|
1975
|
-
yenBanknote: "\u{1F4B4}",
|
|
1976
|
-
yinYang: "\u262F\uFE0F",
|
|
1977
|
-
yoYo: "\u{1FA80}",
|
|
1978
|
-
zanyFace: "\u{1F92A}",
|
|
1979
|
-
zebra: "\u{1F993}",
|
|
1980
|
-
zipperMouthFace: "\u{1F910}",
|
|
1981
|
-
zombie: "\u{1F9DF}",
|
|
1982
|
-
zzz: "\u{1F4A4}"
|
|
1983
|
-
};
|
|
1984
|
-
|
|
1985
|
-
// src/emoji/index.ts
|
|
1986
|
-
var aliases = {
|
|
1987
|
-
love: GeneratedEmoji.redHeart,
|
|
1988
|
-
like: GeneratedEmoji.thumbsUp,
|
|
1989
|
-
dislike: GeneratedEmoji.thumbsDown,
|
|
1990
|
-
laugh: GeneratedEmoji.faceWithTearsOfJoy,
|
|
1991
|
-
emphasize: GeneratedEmoji.doubleExclamationMark,
|
|
1992
|
-
question: GeneratedEmoji.redQuestionMark
|
|
1993
|
-
};
|
|
1994
|
-
var Emoji = { ...GeneratedEmoji, ...aliases };
|
|
1995
|
-
|
|
1996
|
-
// src/spectrum.ts
|
|
1997
|
-
import {
|
|
1998
|
-
createLogger as createLogger3,
|
|
1999
|
-
setupOtel,
|
|
2000
|
-
withSpan
|
|
2001
|
-
} from "@photon-ai/otel";
|
|
2002
|
-
import { RawInboundEvent } from "@photon-ai/proto/photon/fusor/v1/inbound";
|
|
2003
|
-
import z2 from "zod";
|
|
2004
|
-
|
|
2005
|
-
// src/build-env.ts
|
|
2006
|
-
var SPECTRUM_SDK_VERSION = "local";
|
|
2007
|
-
var SPECTRUM_BUILD_ENV = "development";
|
|
2008
|
-
|
|
2009
|
-
// src/fusor/core.ts
|
|
2010
|
-
import { createLogger as createLogger2 } from "@photon-ai/otel";
|
|
2011
|
-
|
|
2012
|
-
// src/fusor/auth.ts
|
|
2013
|
-
var RENEWAL_RATIO = 0.8;
|
|
2014
|
-
var EXPIRY_BUFFER_MS = 3e4;
|
|
2015
|
-
var RETRY_DELAY_MS = 3e4;
|
|
2016
|
-
function createFusorTokenProvider(projectId, projectSecret) {
|
|
2017
|
-
return (async () => {
|
|
2018
|
-
let tokenData = await cloud.issueFusorToken(projectId, projectSecret);
|
|
2019
|
-
let tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
|
|
2020
|
-
let disposed = false;
|
|
2021
|
-
let renewalTimer;
|
|
2022
|
-
const clearRenewalTimer = () => {
|
|
2023
|
-
if (renewalTimer !== void 0) {
|
|
2024
|
-
clearTimeout(renewalTimer);
|
|
2025
|
-
renewalTimer = void 0;
|
|
2026
|
-
}
|
|
2027
|
-
};
|
|
2028
|
-
const refresh = async () => {
|
|
2029
|
-
tokenData = await cloud.issueFusorToken(projectId, projectSecret);
|
|
2030
|
-
tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
|
|
2031
|
-
};
|
|
2032
|
-
const scheduleRetry = () => {
|
|
2033
|
-
if (disposed) {
|
|
2034
|
-
return;
|
|
2035
|
-
}
|
|
2036
|
-
clearRenewalTimer();
|
|
2037
|
-
renewalTimer = setTimeout(async () => {
|
|
2038
|
-
if (disposed) {
|
|
2039
|
-
return;
|
|
2040
|
-
}
|
|
2041
|
-
try {
|
|
2042
|
-
await refresh();
|
|
2043
|
-
scheduleRenewal();
|
|
2044
|
-
} catch (retryErr) {
|
|
2045
|
-
console.warn(
|
|
2046
|
-
`[spectrum-ts] Fusor token refresh failed; retrying in ${RETRY_DELAY_MS}ms.`,
|
|
2047
|
-
retryErr
|
|
2048
|
-
);
|
|
2049
|
-
scheduleRetry();
|
|
2050
|
-
}
|
|
2051
|
-
}, RETRY_DELAY_MS);
|
|
2052
|
-
renewalTimer?.unref?.();
|
|
2053
|
-
};
|
|
2054
|
-
const scheduleRenewal = () => {
|
|
2055
|
-
if (disposed) {
|
|
2056
|
-
return;
|
|
2057
|
-
}
|
|
2058
|
-
clearRenewalTimer();
|
|
2059
|
-
const ttlMs = tokenData.expiresIn * 1e3;
|
|
2060
|
-
const renewInMs = Math.max(ttlMs * RENEWAL_RATIO, 5e3);
|
|
2061
|
-
renewalTimer = setTimeout(async () => {
|
|
2062
|
-
try {
|
|
2063
|
-
await refresh();
|
|
2064
|
-
scheduleRenewal();
|
|
2065
|
-
} catch (err) {
|
|
2066
|
-
console.warn(
|
|
2067
|
-
`[spectrum-ts] Fusor token refresh failed; retrying in ${RETRY_DELAY_MS}ms.`,
|
|
2068
|
-
err
|
|
2069
|
-
);
|
|
2070
|
-
scheduleRetry();
|
|
2071
|
-
}
|
|
2072
|
-
}, renewInMs);
|
|
2073
|
-
renewalTimer?.unref?.();
|
|
2074
|
-
};
|
|
2075
|
-
scheduleRenewal();
|
|
2076
|
-
return {
|
|
2077
|
-
async getToken() {
|
|
2078
|
-
if (Date.now() >= tokenExpiresAt - EXPIRY_BUFFER_MS) {
|
|
2079
|
-
await refresh();
|
|
2080
|
-
scheduleRenewal();
|
|
2081
|
-
}
|
|
2082
|
-
return tokenData.token;
|
|
2083
|
-
},
|
|
2084
|
-
invalidate() {
|
|
2085
|
-
tokenExpiresAt = 0;
|
|
2086
|
-
},
|
|
2087
|
-
async dispose() {
|
|
2088
|
-
disposed = true;
|
|
2089
|
-
clearRenewalTimer();
|
|
2090
|
-
}
|
|
2091
|
-
};
|
|
2092
|
-
})();
|
|
2093
|
-
}
|
|
2094
|
-
|
|
2095
|
-
// src/fusor/parse.ts
|
|
2096
|
-
var CR = 13;
|
|
2097
|
-
var LF = 10;
|
|
2098
|
-
function findHeaderEnd(bytes) {
|
|
2099
|
-
for (let i = 0; i + 3 < bytes.length; i++) {
|
|
2100
|
-
if (bytes[i] === CR && bytes[i + 1] === LF && bytes[i + 2] === CR && bytes[i + 3] === LF) {
|
|
2101
|
-
return i;
|
|
2102
|
-
}
|
|
2103
|
-
}
|
|
2104
|
-
return -1;
|
|
2105
|
-
}
|
|
2106
|
-
function parseHttpRequest(bytes) {
|
|
2107
|
-
const headerEnd = findHeaderEnd(bytes);
|
|
2108
|
-
if (headerEnd < 0) {
|
|
2109
|
-
throw new Error("fusor: raw_request missing CRLFCRLF header terminator");
|
|
2110
|
-
}
|
|
2111
|
-
const headerText = new TextDecoder("utf-8").decode(
|
|
2112
|
-
bytes.subarray(0, headerEnd)
|
|
2113
|
-
);
|
|
2114
|
-
const rawBody = bytes.subarray(headerEnd + 4);
|
|
2115
|
-
const lines = headerText.split("\r\n");
|
|
2116
|
-
const requestLine = lines[0];
|
|
2117
|
-
if (!requestLine) {
|
|
2118
|
-
throw new Error("fusor: raw_request missing request line");
|
|
2119
|
-
}
|
|
2120
|
-
const firstSpace = requestLine.indexOf(" ");
|
|
2121
|
-
const lastSpace = requestLine.lastIndexOf(" ");
|
|
2122
|
-
if (firstSpace < 0 || lastSpace <= firstSpace) {
|
|
2123
|
-
throw new Error(`fusor: malformed request line: ${requestLine}`);
|
|
2124
|
-
}
|
|
2125
|
-
const method = requestLine.slice(0, firstSpace);
|
|
2126
|
-
const path = requestLine.slice(firstSpace + 1, lastSpace);
|
|
2127
|
-
const headers = {};
|
|
2128
|
-
for (let i = 1; i < lines.length; i++) {
|
|
2129
|
-
const line = lines[i];
|
|
2130
|
-
if (!line) {
|
|
2131
|
-
continue;
|
|
2132
|
-
}
|
|
2133
|
-
const colon = line.indexOf(":");
|
|
2134
|
-
if (colon < 0) {
|
|
2135
|
-
continue;
|
|
2136
|
-
}
|
|
2137
|
-
const key = line.slice(0, colon).trim().toLowerCase();
|
|
2138
|
-
const value = line.slice(colon + 1).trim();
|
|
2139
|
-
if (!key) {
|
|
2140
|
-
continue;
|
|
2141
|
-
}
|
|
2142
|
-
const existing = headers[key];
|
|
2143
|
-
headers[key] = existing ? `${existing}, ${value}` : value;
|
|
2144
|
-
}
|
|
2145
|
-
return { method, path, headers, rawBody };
|
|
2146
|
-
}
|
|
2147
|
-
|
|
2148
|
-
// src/fusor/websocket.ts
|
|
2149
|
-
import { createLogger } from "@photon-ai/otel";
|
|
2150
|
-
var log = createLogger("spectrum.fusor.ws");
|
|
2151
|
-
var FUSOR_WS_SUBPROTOCOL = "fusor.v1.json";
|
|
2152
|
-
var DEFAULT_HEARTBEAT_INTERVAL_MS = 3e4;
|
|
2153
|
-
var STALENESS_GRACE_MS = 5e3;
|
|
2154
|
-
var FusorWsError = class extends Error {
|
|
2155
|
-
closeCode;
|
|
2156
|
-
errorCode;
|
|
2157
|
-
constructor(message, closeCode, errorCode) {
|
|
2158
|
-
super(message);
|
|
2159
|
-
this.name = "FusorWsError";
|
|
2160
|
-
this.closeCode = closeCode;
|
|
2161
|
-
this.errorCode = errorCode;
|
|
2162
|
-
}
|
|
2163
|
-
};
|
|
2164
|
-
function isWsAuthError(error) {
|
|
2165
|
-
return error instanceof FusorWsError && (error.closeCode === 4401 || error.errorCode === "unauthenticated");
|
|
2166
|
-
}
|
|
2167
|
-
function decodeBase64(value) {
|
|
2168
|
-
if (typeof Buffer !== "undefined") {
|
|
2169
|
-
return new Uint8Array(Buffer.from(value, "base64"));
|
|
2170
|
-
}
|
|
2171
|
-
return Uint8Array.from(atob(value), (c) => c.charCodeAt(0));
|
|
2172
|
-
}
|
|
2173
|
-
function encodeBase64(bytes) {
|
|
2174
|
-
if (typeof Buffer !== "undefined") {
|
|
2175
|
-
return Buffer.from(bytes).toString("base64");
|
|
2176
|
-
}
|
|
2177
|
-
const chunkSize = 32768;
|
|
2178
|
-
const parts = [];
|
|
2179
|
-
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
2180
|
-
parts.push(String.fromCharCode(...bytes.subarray(i, i + chunkSize)));
|
|
2181
|
-
}
|
|
2182
|
-
return btoa(parts.join(""));
|
|
2183
|
-
}
|
|
2184
|
-
function toRawInboundEvent(frame) {
|
|
2185
|
-
const e = frame.event;
|
|
2186
|
-
return {
|
|
2187
|
-
eventId: e.eventId,
|
|
2188
|
-
projectId: e.projectId,
|
|
2189
|
-
platform: e.platform,
|
|
2190
|
-
receivedAt: e.receivedAt ? new Date(e.receivedAt) : void 0,
|
|
2191
|
-
sourceId: e.sourceId ?? "",
|
|
2192
|
-
prevSubjectSeq: e.prevSubjectSeq ?? 0,
|
|
2193
|
-
rawRequest: decodeBase64(e.rawRequest)
|
|
2194
|
-
};
|
|
2195
|
-
}
|
|
2196
|
-
function runFusorWsSession(options) {
|
|
2197
|
-
const WebSocketCtor = globalThis.WebSocket;
|
|
2198
|
-
if (typeof WebSocketCtor !== "function") {
|
|
2199
|
-
throw new FusorWsError(
|
|
2200
|
-
"global WebSocket is not available in this runtime \u2014 the fusor websocket transport needs Bun, Node >= 22, or a browser/worker environment"
|
|
2201
|
-
);
|
|
2202
|
-
}
|
|
2203
|
-
const wsOpen = WebSocketCtor.OPEN;
|
|
2204
|
-
const ws = new WebSocketCtor(options.url, [FUSOR_WS_SUBPROTOCOL]);
|
|
2205
|
-
let settled = false;
|
|
2206
|
-
let closedByUs = false;
|
|
2207
|
-
let pendingError = null;
|
|
2208
|
-
let stalenessBudgetMs = 2 * DEFAULT_HEARTBEAT_INTERVAL_MS + STALENESS_GRACE_MS;
|
|
2209
|
-
let watchdog;
|
|
2210
|
-
let tail = Promise.resolve();
|
|
2211
|
-
let resolveDone;
|
|
2212
|
-
let rejectDone;
|
|
2213
|
-
const done = new Promise((resolve, reject) => {
|
|
2214
|
-
resolveDone = resolve;
|
|
2215
|
-
rejectDone = reject;
|
|
2216
|
-
});
|
|
2217
|
-
const settle = (error) => {
|
|
2218
|
-
if (settled) {
|
|
2219
|
-
return;
|
|
2220
|
-
}
|
|
2221
|
-
settled = true;
|
|
2222
|
-
if (watchdog) {
|
|
2223
|
-
clearTimeout(watchdog);
|
|
2224
|
-
watchdog = void 0;
|
|
2225
|
-
}
|
|
2226
|
-
if (error) {
|
|
2227
|
-
rejectDone(error);
|
|
2228
|
-
} else {
|
|
2229
|
-
resolveDone();
|
|
2230
|
-
}
|
|
2231
|
-
};
|
|
2232
|
-
const armWatchdog = () => {
|
|
2233
|
-
if (settled) {
|
|
2234
|
-
return;
|
|
2235
|
-
}
|
|
2236
|
-
if (watchdog) {
|
|
2237
|
-
clearTimeout(watchdog);
|
|
2238
|
-
}
|
|
2239
|
-
watchdog = setTimeout(() => {
|
|
2240
|
-
log.warn("fusor ws: no frame within staleness budget; closing", {
|
|
2241
|
-
budgetMs: stalenessBudgetMs
|
|
2242
|
-
});
|
|
2243
|
-
settle(new FusorWsError("websocket heartbeat timeout"));
|
|
2244
|
-
try {
|
|
2245
|
-
ws.close();
|
|
2246
|
-
} catch {
|
|
2247
|
-
}
|
|
2248
|
-
}, stalenessBudgetMs);
|
|
2249
|
-
watchdog.unref?.();
|
|
2250
|
-
};
|
|
2251
|
-
const sendReplyFor = (eventId) => (reply2) => {
|
|
2252
|
-
if (ws.readyState !== wsOpen) {
|
|
2253
|
-
return;
|
|
2254
|
-
}
|
|
2255
|
-
ws.send(
|
|
2256
|
-
JSON.stringify({
|
|
2257
|
-
type: "reply",
|
|
2258
|
-
eventId,
|
|
2259
|
-
status: reply2.status,
|
|
2260
|
-
headers: reply2.headers,
|
|
2261
|
-
...reply2.body.length > 0 && { body: encodeBase64(reply2.body) },
|
|
2262
|
-
...reply2.errorReason && { errorReason: reply2.errorReason }
|
|
2263
|
-
})
|
|
2264
|
-
);
|
|
2265
|
-
};
|
|
2266
|
-
const handleReadyFrame = (frame) => {
|
|
2267
|
-
const interval = frame.heartbeatIntervalMs;
|
|
2268
|
-
if (typeof interval === "number" && interval > 0) {
|
|
2269
|
-
stalenessBudgetMs = 2 * interval + STALENESS_GRACE_MS;
|
|
2270
|
-
}
|
|
2271
|
-
log.info("fusor ws stream ready", {
|
|
2272
|
-
projectId: typeof frame.projectId === "string" ? frame.projectId : "",
|
|
2273
|
-
heartbeatIntervalMs: typeof interval === "number" ? interval : 0
|
|
2274
|
-
});
|
|
2275
|
-
};
|
|
2276
|
-
const handleEventFrame = (frame) => {
|
|
2277
|
-
const eventFrame = frame;
|
|
2278
|
-
let event;
|
|
2279
|
-
try {
|
|
2280
|
-
event = toRawInboundEvent(eventFrame);
|
|
2281
|
-
} catch (error) {
|
|
2282
|
-
log.warn("fusor ws: undecodable event frame; skipping", {
|
|
2283
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2284
|
-
});
|
|
2285
|
-
return;
|
|
2286
|
-
}
|
|
2287
|
-
const sendReply = eventFrame.replyExpected ? sendReplyFor(event.eventId) : void 0;
|
|
2288
|
-
tail = tail.then(() => options.onEvent(event, sendReply)).catch((error) => {
|
|
2289
|
-
log.warn("fusor ws: event handler failed", {
|
|
2290
|
-
eventId: event.eventId,
|
|
2291
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2292
|
-
});
|
|
2293
|
-
});
|
|
2294
|
-
};
|
|
2295
|
-
const handleErrorFrame = (frame) => {
|
|
2296
|
-
const code = typeof frame.code === "string" ? frame.code : "unknown";
|
|
2297
|
-
const message = typeof frame.message === "string" ? frame.message : "server error";
|
|
2298
|
-
const reason = typeof frame.reason === "string" ? frame.reason : void 0;
|
|
2299
|
-
if (frame.fatal === true) {
|
|
2300
|
-
pendingError = { code, message, reason };
|
|
2301
|
-
} else {
|
|
2302
|
-
log.warn("fusor ws: server notice", { code, message, reason });
|
|
2303
|
-
}
|
|
2304
|
-
};
|
|
2305
|
-
const handleFrame = (raw) => {
|
|
2306
|
-
if (typeof raw !== "string") {
|
|
2307
|
-
return;
|
|
2308
|
-
}
|
|
2309
|
-
let frame;
|
|
2310
|
-
try {
|
|
2311
|
-
frame = JSON.parse(raw);
|
|
2312
|
-
} catch {
|
|
2313
|
-
log.warn("fusor ws: unparseable server frame; ignoring");
|
|
2314
|
-
return;
|
|
2315
|
-
}
|
|
2316
|
-
switch (frame.type) {
|
|
2317
|
-
case "ready":
|
|
2318
|
-
handleReadyFrame(frame);
|
|
2319
|
-
return;
|
|
2320
|
-
case "event":
|
|
2321
|
-
handleEventFrame(frame);
|
|
2322
|
-
return;
|
|
2323
|
-
case "error":
|
|
2324
|
-
handleErrorFrame(frame);
|
|
2325
|
-
return;
|
|
2326
|
-
default:
|
|
2327
|
-
return;
|
|
2328
|
-
}
|
|
2329
|
-
};
|
|
2330
|
-
ws.onopen = () => {
|
|
2331
|
-
armWatchdog();
|
|
2332
|
-
ws.send(
|
|
2333
|
-
JSON.stringify({ type: "init", startSeq: 0, token: options.token })
|
|
2334
|
-
);
|
|
2335
|
-
};
|
|
2336
|
-
ws.onmessage = (messageEvent) => {
|
|
2337
|
-
armWatchdog();
|
|
2338
|
-
handleFrame(messageEvent.data);
|
|
2339
|
-
};
|
|
2340
|
-
ws.onerror = () => {
|
|
2341
|
-
log.debug("fusor ws: socket error event");
|
|
2342
|
-
};
|
|
2343
|
-
ws.onclose = (closeEvent) => {
|
|
2344
|
-
if (closedByUs) {
|
|
2345
|
-
settle();
|
|
2346
|
-
return;
|
|
2347
|
-
}
|
|
2348
|
-
const detail = pendingError ? `${pendingError.code}${pendingError.reason ? `:${pendingError.reason}` : ""} \u2014 ${pendingError.message}` : closeEvent.reason || "connection closed";
|
|
2349
|
-
settle(
|
|
2350
|
-
new FusorWsError(
|
|
2351
|
-
`fusor websocket closed (${closeEvent.code}): ${detail}`,
|
|
2352
|
-
closeEvent.code,
|
|
2353
|
-
pendingError?.code ?? (closeEvent.reason || void 0)
|
|
2354
|
-
)
|
|
2355
|
-
);
|
|
2356
|
-
};
|
|
2357
|
-
armWatchdog();
|
|
2358
|
-
return {
|
|
2359
|
-
done,
|
|
2360
|
-
close() {
|
|
2361
|
-
closedByUs = true;
|
|
2362
|
-
try {
|
|
2363
|
-
ws.close(1e3);
|
|
2364
|
-
} catch {
|
|
2365
|
-
}
|
|
2366
|
-
const failsafe = setTimeout(() => settle(), 2e3);
|
|
2367
|
-
failsafe.unref?.();
|
|
2368
|
-
}
|
|
2369
|
-
};
|
|
2370
|
-
}
|
|
2371
|
-
|
|
2372
|
-
// src/fusor/core.ts
|
|
2373
|
-
var DEFAULT_FUSOR_WS_URL = "wss://fusor-ws.spectrum.photon.codes/v1/subscribe";
|
|
2374
|
-
var RECONNECT_BASE_MS = 1e3;
|
|
2375
|
-
var RECONNECT_MAX_MS = 3e4;
|
|
2376
|
-
var log2 = createLogger2("spectrum.fusor");
|
|
2377
|
-
var errorText = (error) => error instanceof Error ? error.message : String(error);
|
|
2378
|
-
function toReplyBytes(body) {
|
|
2379
|
-
if (body === void 0) {
|
|
2380
|
-
return new Uint8Array(0);
|
|
2381
|
-
}
|
|
2382
|
-
if (typeof body === "string") {
|
|
2383
|
-
return new TextEncoder().encode(body);
|
|
2384
|
-
}
|
|
2385
|
-
return body;
|
|
2386
|
-
}
|
|
2387
|
-
function combineReplies(outcomes) {
|
|
2388
|
-
const successes = outcomes.filter((o) => o.ok);
|
|
2389
|
-
if (successes.length === 0) {
|
|
2390
|
-
const firstFailure = outcomes[0];
|
|
2391
|
-
return {
|
|
2392
|
-
eventId: "",
|
|
2393
|
-
errorReason: firstFailure?.errorReason ?? "no handler succeeded",
|
|
2394
|
-
status: 0,
|
|
2395
|
-
headers: {},
|
|
2396
|
-
body: new Uint8Array(0)
|
|
2397
|
-
};
|
|
2398
|
-
}
|
|
2399
|
-
let status = 0;
|
|
2400
|
-
const headers = {};
|
|
2401
|
-
let body = new Uint8Array(0);
|
|
2402
|
-
for (const outcome of successes) {
|
|
2403
|
-
const reply2 = outcome.reply;
|
|
2404
|
-
if (!reply2) {
|
|
2405
|
-
continue;
|
|
2406
|
-
}
|
|
2407
|
-
if (reply2.status !== void 0 && reply2.status > status) {
|
|
2408
|
-
status = reply2.status;
|
|
2409
|
-
}
|
|
2410
|
-
if (reply2.headers) {
|
|
2411
|
-
for (const [k, v] of Object.entries(reply2.headers)) {
|
|
2412
|
-
headers[k.toLowerCase()] = v;
|
|
2413
|
-
}
|
|
2414
|
-
}
|
|
2415
|
-
const candidate = toReplyBytes(reply2.body);
|
|
2416
|
-
if (candidate.length > 0) {
|
|
2417
|
-
body = candidate;
|
|
2418
|
-
}
|
|
2419
|
-
}
|
|
2420
|
-
return {
|
|
2421
|
-
eventId: "",
|
|
2422
|
-
errorReason: "",
|
|
2423
|
-
status,
|
|
2424
|
-
headers,
|
|
2425
|
-
body
|
|
2426
|
-
};
|
|
2427
|
-
}
|
|
2428
|
-
function routeHandlerResult(result, handler, deliver) {
|
|
2429
|
-
if (result === void 0) {
|
|
2430
|
-
return;
|
|
2431
|
-
}
|
|
2432
|
-
const items = Array.isArray(result) ? result : [result];
|
|
2433
|
-
for (const item of items) {
|
|
2434
|
-
if (!isFusorEvent(item)) {
|
|
2435
|
-
deliver(item);
|
|
2436
|
-
continue;
|
|
2437
|
-
}
|
|
2438
|
-
if (item.name === FUSOR_MESSAGES_CHANNEL) {
|
|
2439
|
-
deliver(item.data);
|
|
2440
|
-
} else {
|
|
2441
|
-
handler.pushEvent(item.name, item.data);
|
|
2442
|
-
}
|
|
2443
|
-
}
|
|
2444
|
-
}
|
|
2445
|
-
function runHandlerOnce(handler, parsedRequest, deliver = handler.pushMessage) {
|
|
2446
|
-
return (async () => {
|
|
2447
|
-
try {
|
|
2448
|
-
const payload = await handler.verify(parsedRequest);
|
|
2449
|
-
let reply2;
|
|
2450
|
-
let respondCalled = false;
|
|
2451
|
-
let returned = false;
|
|
2452
|
-
const respond = (next) => {
|
|
2453
|
-
if (returned) {
|
|
2454
|
-
log2.warn("fusor.respond called after handler returned; ignoring");
|
|
2455
|
-
return;
|
|
2456
|
-
}
|
|
2457
|
-
if (respondCalled) {
|
|
2458
|
-
log2.debug("fusor.respond called more than once; last call wins");
|
|
2459
|
-
}
|
|
2460
|
-
respondCalled = true;
|
|
2461
|
-
reply2 = next;
|
|
2462
|
-
};
|
|
2463
|
-
const result = await handler.messages({ payload, respond });
|
|
2464
|
-
returned = true;
|
|
2465
|
-
routeHandlerResult(result, handler, deliver);
|
|
2466
|
-
return { ok: true, reply: reply2 };
|
|
2467
|
-
} catch (error) {
|
|
2468
|
-
return { ok: false, errorReason: errorText(error) };
|
|
2469
|
-
}
|
|
2470
|
-
})();
|
|
2471
|
-
}
|
|
2472
|
-
var FusorCore = class {
|
|
2473
|
-
options;
|
|
2474
|
-
websocketEndpoint;
|
|
2475
|
-
handlers = /* @__PURE__ */ new Map();
|
|
2476
|
-
tokenProvider;
|
|
2477
|
-
wsSession;
|
|
2478
|
-
connectionLoop;
|
|
2479
|
-
started = false;
|
|
2480
|
-
stopped = false;
|
|
2481
|
-
stopResolve;
|
|
2482
|
-
stoppedPromise;
|
|
2483
|
-
// The reconnect backoff sleep, made cancelable so close() can wake it.
|
|
2484
|
-
reconnectTimer;
|
|
2485
|
-
reconnectResolve;
|
|
2486
|
-
constructor(options) {
|
|
2487
|
-
this.options = options;
|
|
2488
|
-
this.websocketEndpoint = options.websocketEndpoint ?? process.env.SPECTRUM_FUSOR_WS_URL ?? DEFAULT_FUSOR_WS_URL;
|
|
2489
|
-
this.stoppedPromise = new Promise((resolve) => {
|
|
2490
|
-
this.stopResolve = resolve;
|
|
2491
|
-
});
|
|
2492
|
-
}
|
|
2493
|
-
register(platform, handler) {
|
|
2494
|
-
const list = this.handlers.get(platform) ?? [];
|
|
2495
|
-
list.push(handler);
|
|
2496
|
-
this.handlers.set(platform, list);
|
|
2497
|
-
}
|
|
2498
|
-
async start() {
|
|
2499
|
-
if (!(this.options.projectId && this.options.projectSecret)) {
|
|
2500
|
-
throw new Error(
|
|
2501
|
-
"fusor: streaming via spectrum.messages requires projectId and projectSecret"
|
|
2502
|
-
);
|
|
2503
|
-
}
|
|
2504
|
-
if (this.started) {
|
|
2505
|
-
return;
|
|
2506
|
-
}
|
|
2507
|
-
this.started = true;
|
|
2508
|
-
this.tokenProvider = await createFusorTokenProvider(
|
|
2509
|
-
this.options.projectId,
|
|
2510
|
-
this.options.projectSecret
|
|
2511
|
-
);
|
|
2512
|
-
this.connectionLoop = this.runConnectionLoop().catch((error) => {
|
|
2513
|
-
log2.error("fusor connection loop crashed", { error });
|
|
2514
|
-
});
|
|
2515
|
-
}
|
|
2516
|
-
// Streaming transport: the fusor.v1.json WebSocket plane. A session that
|
|
2517
|
-
// runs to a clean end reconnects immediately; an errored session backs
|
|
2518
|
-
// off exponentially (reset on the next clean run).
|
|
2519
|
-
async runConnectionLoop() {
|
|
2520
|
-
let attempt = 0;
|
|
2521
|
-
while (!this.stopped) {
|
|
2522
|
-
const wsRan = await this.tryWebsocketOnce();
|
|
2523
|
-
if (this.stopped) {
|
|
2524
|
-
return;
|
|
2525
|
-
}
|
|
2526
|
-
if (wsRan) {
|
|
2527
|
-
attempt = 0;
|
|
2528
|
-
continue;
|
|
2529
|
-
}
|
|
2530
|
-
attempt += 1;
|
|
2531
|
-
await this.backoffSleep(this.backoffMs(attempt));
|
|
2532
|
-
}
|
|
2533
|
-
}
|
|
2534
|
-
// True when the stream ran to a clean end; false when it errored (the
|
|
2535
|
-
// loop then backs off before reconnecting).
|
|
2536
|
-
async tryWebsocketOnce() {
|
|
2537
|
-
try {
|
|
2538
|
-
await this.runWebsocketOnce();
|
|
2539
|
-
return true;
|
|
2540
|
-
} catch (error) {
|
|
2541
|
-
if (isWsAuthError(error)) {
|
|
2542
|
-
this.tokenProvider?.invalidate();
|
|
2543
|
-
}
|
|
2544
|
-
if (!this.stopped) {
|
|
2545
|
-
log2.warn("fusor websocket stream errored; reconnecting", {
|
|
2546
|
-
error: errorText(error)
|
|
2547
|
-
});
|
|
2548
|
-
}
|
|
2549
|
-
return false;
|
|
2550
|
-
}
|
|
2551
|
-
}
|
|
2552
|
-
backoffMs(attempt) {
|
|
2553
|
-
return Math.min(RECONNECT_BASE_MS * 2 ** (attempt - 1), RECONNECT_MAX_MS);
|
|
2554
|
-
}
|
|
2555
|
-
// Cancelable sleep: close() clears the timer and resolves it so
|
|
2556
|
-
// shutdown doesn't wait out the (up to 30s) backoff.
|
|
2557
|
-
async backoffSleep(backoff) {
|
|
2558
|
-
await new Promise((resolve) => {
|
|
2559
|
-
this.reconnectResolve = resolve;
|
|
2560
|
-
const timer = setTimeout(resolve, backoff);
|
|
2561
|
-
timer.unref?.();
|
|
2562
|
-
this.reconnectTimer = timer;
|
|
2563
|
-
});
|
|
2564
|
-
this.reconnectTimer = void 0;
|
|
2565
|
-
this.reconnectResolve = void 0;
|
|
2566
|
-
}
|
|
2567
|
-
async runWebsocketOnce() {
|
|
2568
|
-
if (!this.tokenProvider) {
|
|
2569
|
-
throw new Error("fusor: token not initialized");
|
|
2570
|
-
}
|
|
2571
|
-
const token = await this.tokenProvider.getToken();
|
|
2572
|
-
const session = runFusorWsSession({
|
|
2573
|
-
url: this.websocketEndpoint,
|
|
2574
|
-
token,
|
|
2575
|
-
onEvent: async (event, sendReply) => {
|
|
2576
|
-
if (this.stopped) {
|
|
2577
|
-
return;
|
|
2578
|
-
}
|
|
2579
|
-
const reply2 = await this.processEvent(event);
|
|
2580
|
-
sendReply?.(reply2);
|
|
2581
|
-
}
|
|
2582
|
-
});
|
|
2583
|
-
this.wsSession = session;
|
|
2584
|
-
try {
|
|
2585
|
-
await session.done;
|
|
2586
|
-
} finally {
|
|
2587
|
-
this.wsSession = void 0;
|
|
2588
|
-
}
|
|
2589
|
-
}
|
|
2590
|
-
// Transport-independent event processing: route by platform, parse the wire
|
|
2591
|
-
// request, run every registered handler (verify → messages), and combine the
|
|
2592
|
-
// results into a single InboundReply. Returns the reply instead of writing it
|
|
2593
|
-
// anywhere, so both the streaming session (sendReply) and the synchronous
|
|
2594
|
-
// webhook path can drive it. `deliver` controls where produced records go:
|
|
2595
|
-
// the streaming path defaults to each handler's pushMessage (the per-platform
|
|
2596
|
-
// queue feeding spectrum.messages); the webhook path collects them for the
|
|
2597
|
-
// request instead.
|
|
2598
|
-
async processEvent(event, deliver) {
|
|
2599
|
-
const handlers = this.handlers.get(event.platform) ?? [];
|
|
2600
|
-
if (handlers.length === 0) {
|
|
2601
|
-
log2.warn("fusor: no handler for platform", { platform: event.platform });
|
|
2602
|
-
return {
|
|
2603
|
-
eventId: event.eventId,
|
|
2604
|
-
errorReason: `no handler for platform ${event.platform}`,
|
|
2605
|
-
status: 0,
|
|
2606
|
-
headers: {},
|
|
2607
|
-
body: new Uint8Array(0)
|
|
2608
|
-
};
|
|
2609
|
-
}
|
|
2610
|
-
let parsedRequest;
|
|
2611
|
-
try {
|
|
2612
|
-
parsedRequest = parseHttpRequest(event.rawRequest);
|
|
2613
|
-
} catch (error) {
|
|
2614
|
-
const errorReason = errorText(error);
|
|
2615
|
-
log2.warn("fusor: failed to parse raw_request", {
|
|
2616
|
-
platform: event.platform,
|
|
2617
|
-
error: errorReason
|
|
2618
|
-
});
|
|
2619
|
-
return {
|
|
2620
|
-
eventId: event.eventId,
|
|
2621
|
-
errorReason,
|
|
2622
|
-
status: 0,
|
|
2623
|
-
headers: {},
|
|
2624
|
-
body: new Uint8Array(0)
|
|
2625
|
-
};
|
|
2626
|
-
}
|
|
2627
|
-
const outcomes = await Promise.all(
|
|
2628
|
-
handlers.map((handler) => runHandlerOnce(handler, parsedRequest, deliver))
|
|
2629
|
-
);
|
|
2630
|
-
const combined = combineReplies(outcomes);
|
|
2631
|
-
combined.eventId = event.eventId;
|
|
2632
|
-
return combined;
|
|
2633
|
-
}
|
|
2634
|
-
async close() {
|
|
2635
|
-
if (this.stopped) {
|
|
2636
|
-
return;
|
|
2637
|
-
}
|
|
2638
|
-
this.stopped = true;
|
|
2639
|
-
this.wsSession?.close();
|
|
2640
|
-
if (this.reconnectTimer) {
|
|
2641
|
-
clearTimeout(this.reconnectTimer);
|
|
2642
|
-
this.reconnectTimer = void 0;
|
|
2643
|
-
}
|
|
2644
|
-
this.reconnectResolve?.();
|
|
2645
|
-
this.reconnectResolve = void 0;
|
|
2646
|
-
if (this.tokenProvider) {
|
|
2647
|
-
await this.tokenProvider.dispose();
|
|
2648
|
-
}
|
|
2649
|
-
if (this.connectionLoop) {
|
|
2650
|
-
await this.connectionLoop;
|
|
2651
|
-
}
|
|
2652
|
-
this.stopResolve?.();
|
|
2653
|
-
}
|
|
2654
|
-
async waitStopped() {
|
|
2655
|
-
return this.stoppedPromise;
|
|
2656
|
-
}
|
|
2657
|
-
};
|
|
2658
|
-
|
|
2659
|
-
// src/utils/store.ts
|
|
2660
|
-
var isRecordObject = (value) => {
|
|
2661
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2662
|
-
return false;
|
|
2663
|
-
}
|
|
2664
|
-
const prototype = Object.getPrototypeOf(value);
|
|
2665
|
-
return prototype === Object.prototype || prototype === null;
|
|
2666
|
-
};
|
|
2667
|
-
function createStore() {
|
|
2668
|
-
const data = /* @__PURE__ */ new Map();
|
|
2669
|
-
return {
|
|
2670
|
-
set(key, value) {
|
|
2671
|
-
data.set(key, value);
|
|
2672
|
-
},
|
|
2673
|
-
get(key) {
|
|
2674
|
-
return data.get(key);
|
|
2675
|
-
},
|
|
2676
|
-
has(key) {
|
|
2677
|
-
return data.has(key);
|
|
2678
|
-
},
|
|
2679
|
-
delete(key) {
|
|
2680
|
-
return data.delete(key);
|
|
2681
|
-
},
|
|
2682
|
-
clear() {
|
|
2683
|
-
data.clear();
|
|
2684
|
-
},
|
|
2685
|
-
keys() {
|
|
2686
|
-
return Array.from(data.keys());
|
|
2687
|
-
},
|
|
2688
|
-
string(key) {
|
|
2689
|
-
const v = data.get(key);
|
|
2690
|
-
return typeof v === "string" ? v : void 0;
|
|
2691
|
-
},
|
|
2692
|
-
number(key) {
|
|
2693
|
-
const v = data.get(key);
|
|
2694
|
-
return typeof v === "number" ? v : void 0;
|
|
2695
|
-
},
|
|
2696
|
-
bool(key) {
|
|
2697
|
-
const v = data.get(key);
|
|
2698
|
-
return typeof v === "boolean" ? v : void 0;
|
|
2699
|
-
},
|
|
2700
|
-
object(key) {
|
|
2701
|
-
const v = data.get(key);
|
|
2702
|
-
if (!isRecordObject(v)) {
|
|
2703
|
-
return;
|
|
2704
|
-
}
|
|
2705
|
-
return v;
|
|
2706
|
-
},
|
|
2707
|
-
array(key) {
|
|
2708
|
-
const v = data.get(key);
|
|
2709
|
-
return Array.isArray(v) ? v : void 0;
|
|
2710
|
-
}
|
|
2711
|
-
};
|
|
2712
|
-
}
|
|
2713
|
-
|
|
2714
|
-
// src/webhook/deserialize.ts
|
|
2715
|
-
var MESSAGES_EVENT = "messages";
|
|
2716
|
-
var DEFAULT_ATTACHMENT_NAME = "attachment";
|
|
2717
|
-
var DEFAULT_MIME_TYPE = "application/octet-stream";
|
|
2718
|
-
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
2719
|
-
var asString = (value) => typeof value === "string" ? value : "";
|
|
2720
|
-
var asOptionalDate = (value) => typeof value === "string" ? new Date(value) : void 0;
|
|
2721
|
-
function deserializeSpectrumMessage(envelope, ctx) {
|
|
2722
|
-
if (envelope.event !== MESSAGES_EVENT) {
|
|
2723
|
-
return null;
|
|
2724
|
-
}
|
|
2725
|
-
const message = envelope.message;
|
|
2726
|
-
const platform = resolvePlatform(message);
|
|
2727
|
-
if (!platform) {
|
|
2728
|
-
return null;
|
|
2729
|
-
}
|
|
2730
|
-
const spaceRef = { ...message.space };
|
|
2731
|
-
return {
|
|
2732
|
-
platform,
|
|
2733
|
-
record: {
|
|
2734
|
-
id: message.id,
|
|
2735
|
-
direction: "inbound",
|
|
2736
|
-
content: deserializeContent(message.content, platform, spaceRef, ctx),
|
|
2737
|
-
space: spaceRef,
|
|
2738
|
-
sender: message.sender ? { ...message.sender } : void 0,
|
|
2739
|
-
timestamp: asOptionalDate(message.timestamp)
|
|
2740
|
-
}
|
|
2741
|
-
};
|
|
2742
|
-
}
|
|
2743
|
-
var resolvePlatform = (message) => message.platform ?? message.space.platform;
|
|
2744
|
-
var deserializeContent = (content, platform, spaceRef, ctx) => {
|
|
2745
|
-
try {
|
|
2746
|
-
return mapContent(content, platform, spaceRef, ctx);
|
|
2747
|
-
} catch {
|
|
2748
|
-
return asCustom(content);
|
|
2749
|
-
}
|
|
2750
|
-
};
|
|
2751
|
-
var mapContent = (content, platform, spaceRef, ctx) => {
|
|
2752
|
-
const raw = content;
|
|
2753
|
-
switch (content.type) {
|
|
2754
|
-
case "text":
|
|
2755
|
-
return { type: "text", text: asString(raw.text) };
|
|
2756
|
-
case "richlink":
|
|
2757
|
-
return asRichlink({ url: asString(raw.url) });
|
|
2758
|
-
case "contact":
|
|
2759
|
-
return deserializeContact(raw);
|
|
2760
|
-
case "reaction":
|
|
2761
|
-
return deserializeReaction(raw, spaceRef);
|
|
2762
|
-
case "group":
|
|
2763
|
-
return deserializeGroup(raw, platform, spaceRef, ctx);
|
|
2764
|
-
case "attachment":
|
|
2765
|
-
return deserializeAttachment(raw, platform, spaceRef, ctx);
|
|
2766
|
-
default:
|
|
2767
|
-
return asCustom(content);
|
|
2768
|
-
}
|
|
2769
|
-
};
|
|
2770
|
-
var deserializeAttachment = (raw, platform, spaceRef, ctx) => {
|
|
2771
|
-
const id = asString(raw.id);
|
|
2772
|
-
const bytes = ctx.resolveAttachment?.(platform, spaceRef, id);
|
|
2773
|
-
const unavailable = () => Promise.reject(
|
|
2774
|
-
UnsupportedError.action(
|
|
2775
|
-
"getAttachment",
|
|
2776
|
-
platform,
|
|
2777
|
-
`attachment "${id}" arrived without bytes over the Spectrum webhook and "${platform}" exposes no getAttachment`
|
|
2778
|
-
)
|
|
2779
|
-
);
|
|
2780
|
-
return asAttachment({
|
|
2781
|
-
id,
|
|
2782
|
-
name: asString(raw.name) || DEFAULT_ATTACHMENT_NAME,
|
|
2783
|
-
mimeType: asString(raw.mimeType) || DEFAULT_MIME_TYPE,
|
|
2784
|
-
size: typeof raw.size === "number" ? raw.size : void 0,
|
|
2785
|
-
read: bytes ? bytes.read : unavailable,
|
|
2786
|
-
stream: bytes?.stream
|
|
2787
|
-
});
|
|
2788
|
-
};
|
|
2789
|
-
var deserializeReaction = (raw, spaceRef) => (
|
|
2790
|
-
// `target` is a raw record; `wrapNestedContent` wraps it into a Message.
|
|
2791
|
-
{
|
|
2792
|
-
type: "reaction",
|
|
2793
|
-
emoji: asString(raw.emoji),
|
|
2794
|
-
target: buildTargetRecord(raw.target, spaceRef)
|
|
2795
|
-
}
|
|
2796
|
-
);
|
|
2797
|
-
var buildTargetRecord = (target, spaceRef) => {
|
|
2798
|
-
const ref = isRecord(target) ? target : {};
|
|
2799
|
-
return {
|
|
2800
|
-
id: asString(ref.id),
|
|
2801
|
-
// The target's full content is not delivered; the 80-char `contentPreview`
|
|
2802
|
-
// (text targets only) is the best available stand-in.
|
|
2803
|
-
content: { type: "text", text: asString(ref.contentPreview) },
|
|
2804
|
-
space: { ...spaceRef },
|
|
2805
|
-
sender: ref.sender ? { ...ref.sender } : void 0,
|
|
2806
|
-
timestamp: asOptionalDate(ref.timestamp)
|
|
2807
|
-
};
|
|
2808
|
-
};
|
|
2809
|
-
var deserializeGroup = (raw, platform, spaceRef, ctx) => {
|
|
2810
|
-
const rawItems = Array.isArray(raw.items) ? raw.items : [];
|
|
2811
|
-
return {
|
|
2812
|
-
type: "group",
|
|
2813
|
-
items: rawItems.map(
|
|
2814
|
-
(item) => buildItemRecord(item, platform, spaceRef, ctx)
|
|
2815
|
-
)
|
|
2816
|
-
};
|
|
2817
|
-
};
|
|
2818
|
-
var buildItemRecord = (item, platform, spaceRef, ctx) => {
|
|
2819
|
-
const record = isRecord(item) ? item : {};
|
|
2820
|
-
const itemSpace = isRecord(record.space) ? {
|
|
2821
|
-
...record.space,
|
|
2822
|
-
id: asString(record.space.id) || spaceRef.id
|
|
2823
|
-
} : spaceRef;
|
|
2824
|
-
const content = isRecord(record.content) ? deserializeContent(
|
|
2825
|
-
record.content,
|
|
2826
|
-
platform,
|
|
2827
|
-
itemSpace,
|
|
2828
|
-
ctx
|
|
2829
|
-
) : asCustom(record.content);
|
|
2830
|
-
return {
|
|
2831
|
-
id: asString(record.id),
|
|
2832
|
-
content,
|
|
2833
|
-
space: itemSpace,
|
|
2834
|
-
sender: isRecord(record.sender) ? { ...record.sender, id: asString(record.sender.id) } : void 0,
|
|
2835
|
-
timestamp: asOptionalDate(record.timestamp)
|
|
2836
|
-
};
|
|
2837
|
-
};
|
|
2838
|
-
var deserializeContact = (raw) => {
|
|
2839
|
-
const input = {};
|
|
2840
|
-
const name = normalizeContactName(raw.name);
|
|
2841
|
-
if (name) {
|
|
2842
|
-
input.name = name;
|
|
2843
|
-
}
|
|
2844
|
-
const phones = normalizeContactPhones(raw.phones);
|
|
2845
|
-
if (phones) {
|
|
2846
|
-
input.phones = phones;
|
|
2847
|
-
}
|
|
2848
|
-
if (typeof raw.note === "string") {
|
|
2849
|
-
input.note = raw.note;
|
|
2850
|
-
}
|
|
2851
|
-
if (raw.raw !== void 0) {
|
|
2852
|
-
input.raw = raw.raw;
|
|
2853
|
-
}
|
|
2854
|
-
return asContact(input);
|
|
2855
|
-
};
|
|
2856
|
-
var CONTACT_NAME_KEYS = [
|
|
2857
|
-
"formatted",
|
|
2858
|
-
"first",
|
|
2859
|
-
"last",
|
|
2860
|
-
"middle",
|
|
2861
|
-
"prefix",
|
|
2862
|
-
"suffix"
|
|
2863
|
-
];
|
|
2864
|
-
var normalizeContactName = (value) => {
|
|
2865
|
-
if (typeof value === "string") {
|
|
2866
|
-
return { formatted: value };
|
|
2867
|
-
}
|
|
2868
|
-
if (!isRecord(value)) {
|
|
2869
|
-
return;
|
|
2870
|
-
}
|
|
2871
|
-
const name = {};
|
|
2872
|
-
for (const key of CONTACT_NAME_KEYS) {
|
|
2873
|
-
const part = value[key];
|
|
2874
|
-
if (typeof part === "string") {
|
|
2875
|
-
name[key] = part;
|
|
2876
|
-
}
|
|
2877
|
-
}
|
|
2878
|
-
return Object.keys(name).length > 0 ? name : void 0;
|
|
2879
|
-
};
|
|
2880
|
-
var normalizeContactPhones = (value) => {
|
|
2881
|
-
if (!Array.isArray(value)) {
|
|
2882
|
-
return;
|
|
2883
|
-
}
|
|
2884
|
-
const phones = [];
|
|
2885
|
-
for (const entry of value) {
|
|
2886
|
-
if (typeof entry === "string") {
|
|
2887
|
-
phones.push({ value: entry });
|
|
2888
|
-
} else if (isRecord(entry) && typeof entry.value === "string") {
|
|
2889
|
-
phones.push({ value: entry.value });
|
|
2890
|
-
}
|
|
2891
|
-
}
|
|
2892
|
-
return phones.length > 0 ? phones : void 0;
|
|
2893
|
-
};
|
|
2894
|
-
|
|
2895
|
-
// src/webhook/types.ts
|
|
2896
|
-
import z from "zod";
|
|
2897
|
-
var slimSenderSchema = z.looseObject({
|
|
2898
|
-
id: z.string(),
|
|
2899
|
-
platform: z.string().optional()
|
|
2900
|
-
});
|
|
2901
|
-
var slimSpaceSchema = z.looseObject({
|
|
2902
|
-
id: z.string(),
|
|
2903
|
-
platform: z.string().optional()
|
|
2904
|
-
});
|
|
2905
|
-
var slimContentSchema = z.looseObject({
|
|
2906
|
-
type: z.string()
|
|
2907
|
-
});
|
|
2908
|
-
var slimMessageRefSchema = z.looseObject({
|
|
2909
|
-
id: z.string(),
|
|
2910
|
-
platform: z.string().optional(),
|
|
2911
|
-
timestamp: z.string().optional(),
|
|
2912
|
-
sender: slimSenderSchema.optional(),
|
|
2913
|
-
contentPreview: z.string().optional()
|
|
2914
|
-
});
|
|
2915
|
-
var slimMessageSchema = z.looseObject({
|
|
2916
|
-
id: z.string(),
|
|
2917
|
-
platform: z.string().optional(),
|
|
2918
|
-
// Webhooks are inbound-only; `direction` is left loose (not a `"inbound"`
|
|
2919
|
-
// literal) so a future direction value cannot fail an older SDK's parse.
|
|
2920
|
-
direction: z.string().optional(),
|
|
2921
|
-
timestamp: z.string().optional(),
|
|
2922
|
-
sender: slimSenderSchema.optional(),
|
|
2923
|
-
space: slimSpaceSchema,
|
|
2924
|
-
content: slimContentSchema
|
|
2925
|
-
});
|
|
2926
|
-
var slimEnvelopeSchema = z.looseObject({
|
|
2927
|
-
event: z.string(),
|
|
2928
|
-
space: slimSpaceSchema.optional(),
|
|
2929
|
-
message: slimMessageSchema
|
|
2930
|
-
});
|
|
2931
|
-
|
|
2932
|
-
// src/webhook/verify.ts
|
|
2933
|
-
import { createHmac, timingSafeEqual } from "crypto";
|
|
2934
|
-
var SIGNATURE_HEADER = "x-spectrum-signature";
|
|
2935
|
-
var TIMESTAMP_HEADER = "x-spectrum-timestamp";
|
|
2936
|
-
var SIGNATURE_PREFIX = "v0=";
|
|
2937
|
-
var SIGNATURE_SCHEME = "v0";
|
|
2938
|
-
var REPLAY_TOLERANCE_SECONDS = 300;
|
|
2939
|
-
var MILLIS_PER_SECOND = 1e3;
|
|
2940
|
-
function verifySpectrumSignature(input) {
|
|
2941
|
-
const { rawBody, headers, secret, now = Date.now() } = input;
|
|
2942
|
-
const provided = headers[SIGNATURE_HEADER];
|
|
2943
|
-
const timestamp = headers[TIMESTAMP_HEADER];
|
|
2944
|
-
if (!(provided && timestamp)) {
|
|
2945
|
-
return { ok: false, reason: "missing-headers" };
|
|
2946
|
-
}
|
|
2947
|
-
const timestampSeconds = Number(timestamp);
|
|
2948
|
-
if (!Number.isFinite(timestampSeconds)) {
|
|
2949
|
-
return { ok: false, reason: "missing-headers" };
|
|
2950
|
-
}
|
|
2951
|
-
const nowSeconds = Math.floor(now / MILLIS_PER_SECOND);
|
|
2952
|
-
if (Math.abs(nowSeconds - timestampSeconds) > REPLAY_TOLERANCE_SECONDS) {
|
|
2953
|
-
return { ok: false, reason: "expired" };
|
|
2954
|
-
}
|
|
2955
|
-
const base = Buffer.concat([
|
|
2956
|
-
Buffer.from(`${SIGNATURE_SCHEME}:${timestamp}:`, "utf8"),
|
|
2957
|
-
Buffer.from(rawBody)
|
|
2958
|
-
]);
|
|
2959
|
-
const expected = createHmac("sha256", secret).update(base).digest();
|
|
2960
|
-
const providedHex = provided.startsWith(SIGNATURE_PREFIX) ? provided.slice(SIGNATURE_PREFIX.length) : provided;
|
|
2961
|
-
const providedBytes = Buffer.from(providedHex, "hex");
|
|
2962
|
-
if (providedBytes.length !== expected.length || !timingSafeEqual(providedBytes, expected)) {
|
|
2963
|
-
return { ok: false, reason: "signature-mismatch" };
|
|
2964
|
-
}
|
|
2965
|
-
return { ok: true };
|
|
2966
|
-
}
|
|
2967
|
-
|
|
2968
|
-
// src/spectrum.ts
|
|
2969
|
-
var PHOTON_OTEL_ENDPOINT = "https://otlp.photon.codes";
|
|
2970
|
-
var STREAM_CLOSE_TIMEOUT_MS = 5e3;
|
|
2971
|
-
var lifecycleLog = createLogger3("spectrum.lifecycle");
|
|
2972
|
-
var ignoreCleanupError = () => void 0;
|
|
2973
|
-
var spectrumOptionsSchema = z2.object({
|
|
2974
|
-
flattenGroups: z2.boolean().optional()
|
|
2975
|
-
}).optional();
|
|
2976
|
-
var spectrumConfigSchema = z2.union([
|
|
2977
|
-
z2.object({
|
|
2978
|
-
projectId: z2.string().min(1),
|
|
2979
|
-
projectSecret: z2.string().min(1),
|
|
2980
|
-
providers: z2.array(z2.custom()),
|
|
2981
|
-
options: spectrumOptionsSchema,
|
|
2982
|
-
telemetry: z2.boolean().optional(),
|
|
2983
|
-
webhookSecret: z2.string().min(1).optional()
|
|
2984
|
-
}),
|
|
2985
|
-
z2.object({
|
|
2986
|
-
projectId: z2.undefined().optional(),
|
|
2987
|
-
projectSecret: z2.undefined().optional(),
|
|
2988
|
-
providers: z2.array(z2.custom()),
|
|
2989
|
-
options: spectrumOptionsSchema,
|
|
2990
|
-
telemetry: z2.boolean().optional(),
|
|
2991
|
-
webhookSecret: z2.string().min(1).optional()
|
|
2992
|
-
})
|
|
2993
|
-
]);
|
|
2994
|
-
function bootstrapTelemetry(opts) {
|
|
2995
|
-
const headers = {};
|
|
2996
|
-
if (opts.projectId && opts.projectSecret) {
|
|
2997
|
-
const credential = `${opts.projectId}:${opts.projectSecret}`;
|
|
2998
|
-
headers.Authorization = `Basic ${btoa(credential)}`;
|
|
2999
|
-
}
|
|
3000
|
-
const resourceAttributes = {
|
|
3001
|
-
"deployment.environment": process.env.DEPLOYMENT_ENV ?? SPECTRUM_BUILD_ENV
|
|
3002
|
-
};
|
|
3003
|
-
if (opts.projectId) {
|
|
3004
|
-
resourceAttributes["spectrum.project_id"] = opts.projectId;
|
|
3005
|
-
}
|
|
3006
|
-
return setupOtel({
|
|
3007
|
-
serviceName: "spectrum-ts",
|
|
3008
|
-
serviceVersion: SPECTRUM_SDK_VERSION,
|
|
3009
|
-
endpoint: PHOTON_OTEL_ENDPOINT,
|
|
3010
|
-
headers,
|
|
3011
|
-
resourceAttributes
|
|
3012
|
-
});
|
|
3013
|
-
}
|
|
3014
|
-
async function Spectrum(options) {
|
|
3015
|
-
spectrumConfigSchema.parse(options);
|
|
3016
|
-
const {
|
|
3017
|
-
projectId,
|
|
3018
|
-
projectSecret,
|
|
3019
|
-
providers,
|
|
3020
|
-
options: runtimeOptions,
|
|
3021
|
-
telemetry,
|
|
3022
|
-
webhookSecret
|
|
3023
|
-
} = options;
|
|
3024
|
-
const flattenGroups = runtimeOptions?.flattenGroups ?? false;
|
|
3025
|
-
const resolvedWebhookSecret = webhookSecret ?? process.env.SPECTRUM_WEBHOOK_SECRET;
|
|
3026
|
-
const otelHandle = telemetry ? bootstrapTelemetry({ projectId, projectSecret }) : void 0;
|
|
3027
|
-
const projectConfig = projectId !== void 0 && projectSecret !== void 0 ? await cloud.getProject(projectId, projectSecret) : void 0;
|
|
3028
|
-
const platformStates = /* @__PURE__ */ new Map();
|
|
3029
|
-
const fusorMessageSources = /* @__PURE__ */ new Map();
|
|
3030
|
-
const messageBroadcasters = /* @__PURE__ */ new Map();
|
|
3031
|
-
const fusorEventSources = /* @__PURE__ */ new Map();
|
|
3032
|
-
const eventBroadcasters = /* @__PURE__ */ new Map();
|
|
3033
|
-
const customEventStreams = /* @__PURE__ */ new Map();
|
|
3034
|
-
let stopped = false;
|
|
3035
|
-
const adaptIterable = (iterable, project) => stream((emit, end) => {
|
|
3036
|
-
const iterator = iterable[Symbol.asyncIterator]();
|
|
3037
|
-
const pump = (async () => {
|
|
3038
|
-
try {
|
|
3039
|
-
let result = await iterator.next();
|
|
3040
|
-
while (!result.done) {
|
|
3041
|
-
if (project) {
|
|
3042
|
-
await project(result.value, emit);
|
|
3043
|
-
} else {
|
|
3044
|
-
await emit(result.value);
|
|
3045
|
-
}
|
|
3046
|
-
result = await iterator.next();
|
|
3047
|
-
}
|
|
3048
|
-
end();
|
|
3049
|
-
} catch (error) {
|
|
3050
|
-
end(error);
|
|
3051
|
-
}
|
|
3052
|
-
})();
|
|
3053
|
-
return async () => {
|
|
3054
|
-
await iterator.return?.();
|
|
3055
|
-
await pump.catch(ignoreCleanupError);
|
|
3056
|
-
};
|
|
3057
|
-
});
|
|
3058
|
-
const resolveRecordToMessages = async (record, rt) => {
|
|
3059
|
-
const { client, config, definition, store } = rt;
|
|
3060
|
-
const built = await withSpan(
|
|
3061
|
-
"spectrum.message.receive",
|
|
3062
|
-
{
|
|
3063
|
-
"spectrum.provider": definition.name,
|
|
3064
|
-
"spectrum.message.id": record.id,
|
|
3065
|
-
"spectrum.space.id": record.space?.id,
|
|
3066
|
-
...contentAttrs(record.content),
|
|
3067
|
-
...senderAttrs(record.sender)
|
|
3068
|
-
},
|
|
3069
|
-
() => {
|
|
3070
|
-
const spaceRef = {
|
|
3071
|
-
...record.space,
|
|
3072
|
-
__platform: definition.name
|
|
3073
|
-
};
|
|
3074
|
-
const actionCtx = { space: spaceRef, client, config, store };
|
|
3075
|
-
const space2 = buildSpace({
|
|
3076
|
-
spaceRef,
|
|
3077
|
-
extras: {},
|
|
3078
|
-
actionCtx,
|
|
3079
|
-
definition,
|
|
3080
|
-
client,
|
|
3081
|
-
config,
|
|
3082
|
-
store
|
|
3083
|
-
});
|
|
3084
|
-
const normalizedMessage2 = wrapProviderMessage(
|
|
3085
|
-
record,
|
|
3086
|
-
{
|
|
3087
|
-
client,
|
|
3088
|
-
config,
|
|
3089
|
-
definition,
|
|
3090
|
-
space: space2,
|
|
3091
|
-
spaceRef,
|
|
3092
|
-
store
|
|
3093
|
-
},
|
|
3094
|
-
"inbound"
|
|
3095
|
-
);
|
|
3096
|
-
return { space: space2, normalizedMessage: normalizedMessage2 };
|
|
3097
|
-
}
|
|
3098
|
-
);
|
|
3099
|
-
const { space, normalizedMessage } = built;
|
|
3100
|
-
if (flattenGroups && normalizedMessage.content.type === "group") {
|
|
3101
|
-
return normalizedMessage.content.items.map((item) => [
|
|
3102
|
-
space,
|
|
3103
|
-
item
|
|
3104
|
-
]);
|
|
3105
|
-
}
|
|
3106
|
-
return [[space, normalizedMessage]];
|
|
3107
|
-
};
|
|
3108
|
-
const createProviderMessagesStream = (state) => {
|
|
3109
|
-
const { client, config, definition, store } = state;
|
|
3110
|
-
const fusorSource = fusorMessageSources.get(definition.name);
|
|
3111
|
-
const raw = fusorSource ? fusorSource.iterable : definition.messages({
|
|
3112
|
-
client,
|
|
3113
|
-
config,
|
|
3114
|
-
projectConfig,
|
|
3115
|
-
store
|
|
3116
|
-
});
|
|
3117
|
-
return adaptIterable(
|
|
3118
|
-
raw,
|
|
3119
|
-
async (record, emit) => {
|
|
3120
|
-
const tuples = await resolveRecordToMessages(record, {
|
|
3121
|
-
client,
|
|
3122
|
-
config,
|
|
3123
|
-
definition,
|
|
3124
|
-
store
|
|
3125
|
-
});
|
|
3126
|
-
for (const tuple of tuples) {
|
|
3127
|
-
await emit(tuple);
|
|
3128
|
-
}
|
|
3129
|
-
}
|
|
3130
|
-
);
|
|
3131
|
-
};
|
|
3132
|
-
const getOrCreateMessageBroadcast = (state) => {
|
|
3133
|
-
if (stopped) {
|
|
3134
|
-
throw new Error(
|
|
3135
|
-
`Spectrum instance has been stopped; cannot subscribe to "${state.definition.name}" messages`
|
|
3136
|
-
);
|
|
3137
|
-
}
|
|
3138
|
-
const name = state.definition.name;
|
|
3139
|
-
let broadcaster = messageBroadcasters.get(name);
|
|
3140
|
-
if (!broadcaster) {
|
|
3141
|
-
broadcaster = broadcast(createProviderMessagesStream(state));
|
|
3142
|
-
messageBroadcasters.set(name, broadcaster);
|
|
3143
|
-
}
|
|
3144
|
-
return broadcaster;
|
|
3145
|
-
};
|
|
3146
|
-
const getOrCreateEventBroadcast = (platform, channel) => {
|
|
3147
|
-
const queue = fusorEventSources.get(platform)?.get(channel);
|
|
3148
|
-
if (!queue) {
|
|
3149
|
-
return;
|
|
3150
|
-
}
|
|
3151
|
-
if (stopped) {
|
|
3152
|
-
throw new Error(
|
|
3153
|
-
`Spectrum instance has been stopped; cannot subscribe to "${platform}" event "${channel}"`
|
|
3154
|
-
);
|
|
3155
|
-
}
|
|
3156
|
-
const key = `${platform}\0${channel}`;
|
|
3157
|
-
let broadcaster = eventBroadcasters.get(key);
|
|
3158
|
-
if (!broadcaster) {
|
|
3159
|
-
broadcaster = broadcast(adaptIterable(queue.iterable));
|
|
3160
|
-
eventBroadcasters.set(key, broadcaster);
|
|
3161
|
-
}
|
|
3162
|
-
return broadcaster;
|
|
3163
|
-
};
|
|
3164
|
-
await withSpan(
|
|
3165
|
-
"spectrum.init",
|
|
3166
|
-
{
|
|
3167
|
-
"spectrum.provider_count": providers.length,
|
|
3168
|
-
"spectrum.flatten_groups": flattenGroups
|
|
3169
|
-
},
|
|
3170
|
-
async () => {
|
|
3171
|
-
for (const provider of providers) {
|
|
3172
|
-
const providerConfig = provider;
|
|
3173
|
-
const def = providerConfig.__definition;
|
|
3174
|
-
const userConfig = def.config.parse(providerConfig.config);
|
|
3175
|
-
const store = createStore();
|
|
3176
|
-
const client = await withSpan(
|
|
3177
|
-
"spectrum.provider.create_client",
|
|
3178
|
-
{
|
|
3179
|
-
"spectrum.provider": def.name
|
|
3180
|
-
},
|
|
3181
|
-
() => def.lifecycle.createClient({
|
|
3182
|
-
config: userConfig,
|
|
3183
|
-
projectId,
|
|
3184
|
-
projectSecret,
|
|
3185
|
-
projectConfig,
|
|
3186
|
-
store
|
|
3187
|
-
})
|
|
3188
|
-
);
|
|
3189
|
-
const state = {
|
|
3190
|
-
client,
|
|
3191
|
-
config: userConfig,
|
|
3192
|
-
definition: def,
|
|
3193
|
-
store
|
|
3194
|
-
};
|
|
3195
|
-
platformStates.set(def.name, {
|
|
3196
|
-
...state,
|
|
3197
|
-
projectConfig,
|
|
3198
|
-
subscribeMessages: () => getOrCreateMessageBroadcast(state).subscribe(),
|
|
3199
|
-
// Fanout subscription to a fusor event channel. Returns undefined for
|
|
3200
|
-
// regular platforms (no per-channel queue) — callers fall back to the
|
|
3201
|
-
// producer path. Resolved lazily, after the fusor bootstrap below has
|
|
3202
|
-
// created the per-(platform, channel) queues.
|
|
3203
|
-
subscribeEvent: (channel) => getOrCreateEventBroadcast(def.name, channel)?.subscribe()
|
|
3204
|
-
});
|
|
3205
|
-
}
|
|
3206
|
-
}
|
|
3207
|
-
);
|
|
3208
|
-
let fusorCore;
|
|
3209
|
-
let fusorStartPromise;
|
|
3210
|
-
const fusorPlatforms = [];
|
|
3211
|
-
for (const [name, state] of platformStates) {
|
|
3212
|
-
if (isFusorClient(state.client)) {
|
|
3213
|
-
fusorPlatforms.push({ name, client: state.client });
|
|
3214
|
-
}
|
|
3215
|
-
}
|
|
3216
|
-
if (fusorPlatforms.length > 0) {
|
|
3217
|
-
fusorCore = new FusorCore({ projectId, projectSecret });
|
|
3218
|
-
for (const { name, client } of fusorPlatforms) {
|
|
3219
|
-
const queue = createAsyncQueue();
|
|
3220
|
-
fusorMessageSources.set(name, queue);
|
|
3221
|
-
const runtime = platformStates.get(name);
|
|
3222
|
-
if (!runtime) {
|
|
3223
|
-
continue;
|
|
3224
|
-
}
|
|
3225
|
-
const userMessages = runtime.definition.messages;
|
|
3226
|
-
const declaredEvents = runtime.definition.events ?? {};
|
|
3227
|
-
const eventQueues = /* @__PURE__ */ new Map();
|
|
3228
|
-
for (const channel of Object.keys(declaredEvents)) {
|
|
3229
|
-
eventQueues.set(channel, createAsyncQueue());
|
|
3230
|
-
}
|
|
3231
|
-
fusorEventSources.set(name, eventQueues);
|
|
3232
|
-
const handler = {
|
|
3233
|
-
verify: client.verify,
|
|
3234
|
-
// Enrich the transport-level `{ payload, respond }` ctx with the same
|
|
3235
|
-
// runtime context every other platform callback receives, so fusor
|
|
3236
|
-
// handlers can read config/store/projectConfig directly instead of
|
|
3237
|
-
// smuggling state through the payload.
|
|
3238
|
-
messages: async (ctx) => userMessages({
|
|
3239
|
-
...ctx,
|
|
3240
|
-
config: runtime.config,
|
|
3241
|
-
store: runtime.store,
|
|
3242
|
-
projectConfig: runtime.projectConfig
|
|
3243
|
-
}),
|
|
3244
|
-
pushMessage: (record) => queue.push(record),
|
|
3245
|
-
pushEvent: (channel, data) => {
|
|
3246
|
-
const eventQueue = eventQueues.get(channel);
|
|
3247
|
-
if (!eventQueue) {
|
|
3248
|
-
lifecycleLog.warn(
|
|
3249
|
-
`spectrum: fusorEvent("${channel}", \u2026) names a channel not declared in "${name}".events; dropping`,
|
|
3250
|
-
{ platform: name, channel }
|
|
3251
|
-
);
|
|
3252
|
-
return;
|
|
3253
|
-
}
|
|
3254
|
-
eventQueue.push(data);
|
|
3255
|
-
}
|
|
3256
|
-
};
|
|
3257
|
-
fusorCore.register(client.platform, handler);
|
|
3258
|
-
}
|
|
3259
|
-
}
|
|
3260
|
-
const ensureFusorStarted = () => {
|
|
3261
|
-
if (!fusorCore) {
|
|
3262
|
-
return Promise.resolve();
|
|
3263
|
-
}
|
|
3264
|
-
if (!fusorStartPromise) {
|
|
3265
|
-
fusorStartPromise = fusorCore.start();
|
|
3266
|
-
}
|
|
3267
|
-
return fusorStartPromise;
|
|
3268
|
-
};
|
|
3269
|
-
const providerNames = providers.map((p) => p.__definition.name).join(",");
|
|
3270
|
-
lifecycleLog.info("Spectrum started", {
|
|
3271
|
-
providerCount: providers.length,
|
|
3272
|
-
providers: providerNames,
|
|
3273
|
-
telemetry: telemetry === true
|
|
3274
|
-
});
|
|
3275
|
-
const createMessagesStream = () => stream((emit, end) => {
|
|
3276
|
-
ensureFusorStarted().catch((error) => end(error));
|
|
3277
|
-
const merged = mergeStreams(
|
|
3278
|
-
Array.from(
|
|
3279
|
-
platformStates.values(),
|
|
3280
|
-
(runtime) => runtime.subscribeMessages()
|
|
3281
|
-
)
|
|
3282
|
-
);
|
|
3283
|
-
const pump = (async () => {
|
|
3284
|
-
try {
|
|
3285
|
-
for await (const value of merged) {
|
|
3286
|
-
await emit(value);
|
|
3287
|
-
}
|
|
3288
|
-
end();
|
|
3289
|
-
} catch (error) {
|
|
3290
|
-
end(error);
|
|
3291
|
-
}
|
|
3292
|
-
})();
|
|
3293
|
-
return async () => {
|
|
3294
|
-
await merged.close();
|
|
3295
|
-
await pump.catch(ignoreCleanupError);
|
|
3296
|
-
};
|
|
3297
|
-
});
|
|
3298
|
-
const createCustomEventStream = (eventName) => stream((emit, end) => {
|
|
3299
|
-
const providerStreams = [];
|
|
3300
|
-
for (const state of platformStates.values()) {
|
|
3301
|
-
const { client, config, definition, store } = state;
|
|
3302
|
-
let source = state.subscribeEvent?.(eventName);
|
|
3303
|
-
if (!source) {
|
|
3304
|
-
const producer = definition.events?.[eventName];
|
|
3305
|
-
if (typeof producer !== "function") {
|
|
3306
|
-
continue;
|
|
3307
|
-
}
|
|
3308
|
-
source = producer({ client, config, projectConfig, store });
|
|
3309
|
-
}
|
|
3310
|
-
const providerEvents = source;
|
|
3311
|
-
providerStreams.push(
|
|
3312
|
-
adaptIterable(
|
|
3313
|
-
providerEvents,
|
|
3314
|
-
async (value, emit2) => {
|
|
3315
|
-
const annotated = await withSpan(
|
|
3316
|
-
"spectrum.event",
|
|
3317
|
-
{
|
|
3318
|
-
"spectrum.provider": definition.name,
|
|
3319
|
-
"spectrum.event.name": eventName
|
|
3320
|
-
},
|
|
3321
|
-
// Object payloads are flattened and tagged with `platform`. A
|
|
3322
|
-
// primitive/null payload can't be spread (a string would mangle
|
|
3323
|
-
// into indexed chars, a number/bool would vanish), so wrap it
|
|
3324
|
-
// under `payload` instead.
|
|
3325
|
-
() => typeof value === "object" && value !== null ? { ...value, platform: definition.name } : { platform: definition.name, payload: value }
|
|
3326
|
-
);
|
|
3327
|
-
await emit2(annotated);
|
|
3328
|
-
}
|
|
3329
|
-
)
|
|
3330
|
-
);
|
|
3331
|
-
}
|
|
3332
|
-
const merged = mergeStreams(providerStreams);
|
|
3333
|
-
const pump = (async () => {
|
|
3334
|
-
try {
|
|
3335
|
-
for await (const value of merged) {
|
|
3336
|
-
await emit(value);
|
|
3337
|
-
}
|
|
3338
|
-
end();
|
|
3339
|
-
} catch (error) {
|
|
3340
|
-
end(error);
|
|
3341
|
-
}
|
|
3342
|
-
})();
|
|
3343
|
-
return async () => {
|
|
3344
|
-
await merged.close();
|
|
3345
|
-
await pump.catch(ignoreCleanupError);
|
|
3346
|
-
};
|
|
3347
|
-
});
|
|
3348
|
-
const messagesStream = createMessagesStream();
|
|
3349
|
-
const closeFusorSources = () => {
|
|
3350
|
-
for (const queue of fusorMessageSources.values()) {
|
|
3351
|
-
queue.close();
|
|
3352
|
-
}
|
|
3353
|
-
fusorMessageSources.clear();
|
|
3354
|
-
for (const queues of fusorEventSources.values()) {
|
|
3355
|
-
for (const queue of queues.values()) {
|
|
3356
|
-
queue.close();
|
|
3357
|
-
}
|
|
3358
|
-
}
|
|
3359
|
-
fusorEventSources.clear();
|
|
3360
|
-
};
|
|
3361
|
-
const stopOnce = async () => {
|
|
3362
|
-
if (stopped) {
|
|
3363
|
-
return;
|
|
3364
|
-
}
|
|
3365
|
-
stopped = true;
|
|
3366
|
-
const streamShutdowns = [
|
|
3367
|
-
messagesStream.close(),
|
|
3368
|
-
...Array.from(
|
|
3369
|
-
customEventStreams.values(),
|
|
3370
|
-
(eventStream) => eventStream.close()
|
|
3371
|
-
),
|
|
3372
|
-
...Array.from(
|
|
3373
|
-
messageBroadcasters.values(),
|
|
3374
|
-
(broadcaster) => broadcaster.close()
|
|
3375
|
-
),
|
|
3376
|
-
...Array.from(
|
|
3377
|
-
eventBroadcasters.values(),
|
|
3378
|
-
(broadcaster) => broadcaster.close()
|
|
3379
|
-
)
|
|
3380
|
-
];
|
|
3381
|
-
process.off("SIGINT", handleSignal);
|
|
3382
|
-
process.off("SIGTERM", handleSignal);
|
|
3383
|
-
const streamCloseStart = performance.now();
|
|
3384
|
-
const streamSettled = Promise.allSettled(streamShutdowns);
|
|
3385
|
-
let streamTimedOut = false;
|
|
3386
|
-
await Promise.race([
|
|
3387
|
-
streamSettled,
|
|
3388
|
-
new Promise((resolve) => {
|
|
3389
|
-
setTimeout(() => {
|
|
3390
|
-
streamTimedOut = true;
|
|
3391
|
-
resolve();
|
|
3392
|
-
}, STREAM_CLOSE_TIMEOUT_MS).unref();
|
|
3393
|
-
})
|
|
3394
|
-
]);
|
|
3395
|
-
if (streamTimedOut) {
|
|
3396
|
-
lifecycleLog.warn("stream close timed out; proceeding to teardown", {
|
|
3397
|
-
timeoutMs: STREAM_CLOSE_TIMEOUT_MS
|
|
3398
|
-
});
|
|
3399
|
-
}
|
|
3400
|
-
let fusorCloseMs = 0;
|
|
3401
|
-
if (fusorCore) {
|
|
3402
|
-
const fusorCloseStart = performance.now();
|
|
3403
|
-
if (fusorStartPromise) {
|
|
3404
|
-
await fusorStartPromise.catch(ignoreCleanupError);
|
|
3405
|
-
}
|
|
3406
|
-
await fusorCore.close().catch((error) => {
|
|
3407
|
-
lifecycleLog.warn("fusor core close failed", { error });
|
|
3408
|
-
});
|
|
3409
|
-
fusorCloseMs = Math.round(performance.now() - fusorCloseStart);
|
|
3410
|
-
closeFusorSources();
|
|
3411
|
-
}
|
|
3412
|
-
const clientShutdowns = [];
|
|
3413
|
-
for (const state of platformStates.values()) {
|
|
3414
|
-
const destroy = state.definition.lifecycle.destroyClient;
|
|
3415
|
-
if (!destroy) {
|
|
3416
|
-
continue;
|
|
3417
|
-
}
|
|
3418
|
-
clientShutdowns.push(
|
|
3419
|
-
withSpan(
|
|
3420
|
-
"spectrum.provider.destroy_client",
|
|
3421
|
-
{
|
|
3422
|
-
"spectrum.provider": state.definition.name
|
|
3423
|
-
},
|
|
3424
|
-
() => destroy({
|
|
3425
|
-
client: state.client,
|
|
3426
|
-
store: state.store
|
|
3427
|
-
})
|
|
3428
|
-
)
|
|
3429
|
-
);
|
|
3430
|
-
}
|
|
3431
|
-
const clientCloseStart = performance.now();
|
|
3432
|
-
await Promise.allSettled(clientShutdowns);
|
|
3433
|
-
const clientCloseMs = Math.round(performance.now() - clientCloseStart);
|
|
3434
|
-
await streamSettled.catch(() => void 0);
|
|
3435
|
-
const streamCloseMs = Math.round(performance.now() - streamCloseStart);
|
|
3436
|
-
customEventStreams.clear();
|
|
3437
|
-
messageBroadcasters.clear();
|
|
3438
|
-
eventBroadcasters.clear();
|
|
3439
|
-
platformStates.clear();
|
|
3440
|
-
lifecycleLog.info("Spectrum stopped", {
|
|
3441
|
-
providers: providerNames,
|
|
3442
|
-
streamCloseMs,
|
|
3443
|
-
fusorCloseMs,
|
|
3444
|
-
clientCloseMs
|
|
3445
|
-
});
|
|
3446
|
-
if (otelHandle) {
|
|
3447
|
-
await otelHandle.shutdown();
|
|
3448
|
-
}
|
|
3449
|
-
};
|
|
3450
|
-
const handleSignal = () => {
|
|
3451
|
-
setTimeout(() => process.exit(1), 3e3).unref();
|
|
3452
|
-
stopOnce().then(() => process.exit(0)).catch(() => process.exit(1));
|
|
3453
|
-
};
|
|
3454
|
-
process.on("SIGINT", handleSignal);
|
|
3455
|
-
process.on("SIGTERM", handleSignal);
|
|
3456
|
-
const messages = messagesStream;
|
|
3457
|
-
const customEventProxy = new Proxy(
|
|
3458
|
-
{},
|
|
3459
|
-
{
|
|
3460
|
-
get(_target, prop) {
|
|
3461
|
-
let eventStream = customEventStreams.get(prop);
|
|
3462
|
-
if (!eventStream) {
|
|
3463
|
-
eventStream = createCustomEventStream(prop);
|
|
3464
|
-
customEventStreams.set(prop, eventStream);
|
|
3465
|
-
}
|
|
3466
|
-
return eventStream;
|
|
3467
|
-
}
|
|
3468
|
-
}
|
|
3469
|
-
);
|
|
3470
|
-
const encodeText = (s) => new TextEncoder().encode(s);
|
|
3471
|
-
const buildWebhookResult = (asWeb, result) => {
|
|
3472
|
-
if (asWeb) {
|
|
3473
|
-
return new Response(result.body, {
|
|
3474
|
-
status: result.status,
|
|
3475
|
-
headers: result.headers
|
|
3476
|
-
});
|
|
3477
|
-
}
|
|
3478
|
-
return result;
|
|
3479
|
-
};
|
|
3480
|
-
const readWebhookInput = async (request) => {
|
|
3481
|
-
if (typeof Request !== "undefined" && request instanceof Request) {
|
|
3482
|
-
const headers2 = {};
|
|
3483
|
-
for (const [key, value] of request.headers) {
|
|
3484
|
-
headers2[key.toLowerCase()] = value;
|
|
3485
|
-
}
|
|
3486
|
-
return {
|
|
3487
|
-
asWeb: true,
|
|
3488
|
-
bodyBytes: new Uint8Array(await request.arrayBuffer()),
|
|
3489
|
-
headers: headers2
|
|
3490
|
-
};
|
|
3491
|
-
}
|
|
3492
|
-
const raw = request;
|
|
3493
|
-
const bodyBytes = raw.body instanceof ArrayBuffer ? new Uint8Array(raw.body) : raw.body;
|
|
3494
|
-
const headers = {};
|
|
3495
|
-
for (const [key, value] of Object.entries(raw.headers ?? {})) {
|
|
3496
|
-
headers[key.toLowerCase()] = String(value);
|
|
3497
|
-
}
|
|
3498
|
-
return { asWeb: false, bodyBytes, headers };
|
|
3499
|
-
};
|
|
3500
|
-
const deliverWebhookMessages = async (collected, runtime, handler, context) => {
|
|
3501
|
-
for (const record of collected) {
|
|
3502
|
-
const tuples = await resolveRecordToMessages(record, runtime);
|
|
3503
|
-
for (const [space, message] of tuples) {
|
|
3504
|
-
try {
|
|
3505
|
-
await handler(space, message);
|
|
3506
|
-
} catch (error) {
|
|
3507
|
-
lifecycleLog.error(
|
|
3508
|
-
`spectrum.webhook: handler threw (async), ${error}`,
|
|
3509
|
-
{
|
|
3510
|
-
eventId: context.eventId,
|
|
3511
|
-
platform: context.platform,
|
|
3512
|
-
messageId: message.id,
|
|
3513
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3514
|
-
}
|
|
3515
|
-
);
|
|
3516
|
-
}
|
|
3517
|
-
}
|
|
3518
|
-
}
|
|
3519
|
-
};
|
|
3520
|
-
const decodeWebhookEvent = (bodyBytes) => {
|
|
3521
|
-
try {
|
|
3522
|
-
return RawInboundEvent.decode(bodyBytes);
|
|
3523
|
-
} catch (error) {
|
|
3524
|
-
lifecycleLog.warn("spectrum.webhook: undecodable RawInboundEvent body", {
|
|
3525
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3526
|
-
});
|
|
3527
|
-
return null;
|
|
3528
|
-
}
|
|
3529
|
-
};
|
|
3530
|
-
const processWebhookEvent = async (core, event, handler) => {
|
|
3531
|
-
const collected = [];
|
|
3532
|
-
const reply2 = await core.processEvent(event, (record) => {
|
|
3533
|
-
collected.push(record);
|
|
3534
|
-
});
|
|
3535
|
-
if (reply2.errorReason) {
|
|
3536
|
-
return {
|
|
3537
|
-
status: 400,
|
|
3538
|
-
headers: reply2.headers ?? {},
|
|
3539
|
-
body: encodeText(reply2.errorReason)
|
|
3540
|
-
};
|
|
3541
|
-
}
|
|
3542
|
-
const result = {
|
|
3543
|
-
status: reply2.status === 0 ? 200 : reply2.status,
|
|
3544
|
-
headers: reply2.headers ?? {},
|
|
3545
|
-
body: reply2.body ?? new Uint8Array(0)
|
|
3546
|
-
};
|
|
3547
|
-
const runtime = platformStates.get(event.platform);
|
|
3548
|
-
if (runtime && collected.length > 0) {
|
|
3549
|
-
deliverWebhookMessages(collected, runtime, handler, event).catch(
|
|
3550
|
-
(error) => {
|
|
3551
|
-
lifecycleLog.error(
|
|
3552
|
-
`spectrum.webhook: delivery failed (async), ${error}`,
|
|
3553
|
-
{
|
|
3554
|
-
eventId: event.eventId,
|
|
3555
|
-
platform: event.platform,
|
|
3556
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3557
|
-
}
|
|
3558
|
-
);
|
|
3559
|
-
}
|
|
3560
|
-
);
|
|
3561
|
-
}
|
|
3562
|
-
return result;
|
|
3563
|
-
};
|
|
3564
|
-
const looksLikeNativePayload = (bodyBytes) => {
|
|
3565
|
-
for (const byte of bodyBytes) {
|
|
3566
|
-
if (byte === 32 || byte === 9 || byte === 10 || byte === 13) {
|
|
3567
|
-
continue;
|
|
3568
|
-
}
|
|
3569
|
-
return byte === 123;
|
|
3570
|
-
}
|
|
3571
|
-
return false;
|
|
3572
|
-
};
|
|
3573
|
-
const webhookText = (status, text2) => ({
|
|
3574
|
-
status,
|
|
3575
|
-
headers: {},
|
|
3576
|
-
body: encodeText(text2)
|
|
3577
|
-
});
|
|
3578
|
-
const resolveWebhookAttachment = (platform, spaceRef, attachmentId) => {
|
|
3579
|
-
const runtime = platformStates.get(platform);
|
|
3580
|
-
const action = runtime?.definition?.actions?.getAttachment;
|
|
3581
|
-
if (!runtime || typeof action !== "function") {
|
|
3582
|
-
return;
|
|
3583
|
-
}
|
|
3584
|
-
const getAttachment = action;
|
|
3585
|
-
const phone = typeof spaceRef.phone === "string" ? spaceRef.phone : void 0;
|
|
3586
|
-
let cached;
|
|
3587
|
-
const fetchOnce = () => {
|
|
3588
|
-
cached ??= getAttachment(
|
|
3589
|
-
{
|
|
3590
|
-
client: runtime.client,
|
|
3591
|
-
config: runtime.config,
|
|
3592
|
-
store: runtime.store
|
|
3593
|
-
},
|
|
3594
|
-
attachmentId,
|
|
3595
|
-
phone
|
|
3596
|
-
);
|
|
3597
|
-
return cached;
|
|
3598
|
-
};
|
|
3599
|
-
return {
|
|
3600
|
-
read: async () => {
|
|
3601
|
-
const found = await fetchOnce();
|
|
3602
|
-
if (!found) {
|
|
3603
|
-
throw new Error(
|
|
3604
|
-
`Spectrum webhook attachment "${attachmentId}" not found on "${platform}"`
|
|
3605
|
-
);
|
|
3606
|
-
}
|
|
3607
|
-
return found.read();
|
|
3608
|
-
},
|
|
3609
|
-
stream: async () => {
|
|
3610
|
-
const found = await fetchOnce();
|
|
3611
|
-
if (!found?.stream) {
|
|
3612
|
-
throw new Error(
|
|
3613
|
-
`Spectrum webhook attachment "${attachmentId}" has no stream on "${platform}"`
|
|
3614
|
-
);
|
|
3615
|
-
}
|
|
3616
|
-
return found.stream();
|
|
3617
|
-
}
|
|
3618
|
-
};
|
|
3619
|
-
};
|
|
3620
|
-
const handleSpectrumWebhook = async (bodyBytes, headers, handler) => {
|
|
3621
|
-
if (!resolvedWebhookSecret) {
|
|
3622
|
-
lifecycleLog.error(
|
|
3623
|
-
"spectrum.webhook: received a signed Spectrum webhook but no webhookSecret is configured (set Spectrum({ webhookSecret }) or SPECTRUM_WEBHOOK_SECRET)"
|
|
3624
|
-
);
|
|
3625
|
-
return webhookText(500, "webhook secret not configured");
|
|
3626
|
-
}
|
|
3627
|
-
const verification = verifySpectrumSignature({
|
|
3628
|
-
rawBody: bodyBytes,
|
|
3629
|
-
headers,
|
|
3630
|
-
secret: resolvedWebhookSecret
|
|
3631
|
-
});
|
|
3632
|
-
if (!verification.ok) {
|
|
3633
|
-
const status = verification.reason === "missing-headers" ? 400 : 401;
|
|
3634
|
-
return webhookText(status, verification.reason);
|
|
3635
|
-
}
|
|
3636
|
-
let envelope;
|
|
3637
|
-
try {
|
|
3638
|
-
const parsed = JSON.parse(new TextDecoder().decode(bodyBytes));
|
|
3639
|
-
envelope = slimEnvelopeSchema.parse(parsed);
|
|
3640
|
-
} catch (error) {
|
|
3641
|
-
lifecycleLog.warn(
|
|
3642
|
-
`spectrum.webhook: malformed Spectrum webhook payload, ${error}`
|
|
3643
|
-
);
|
|
3644
|
-
return webhookText(400, "malformed payload");
|
|
3645
|
-
}
|
|
3646
|
-
const deserialized = deserializeSpectrumMessage(envelope, {
|
|
3647
|
-
resolveAttachment: resolveWebhookAttachment
|
|
3648
|
-
});
|
|
3649
|
-
if (!deserialized) {
|
|
3650
|
-
return webhookText(200, "ok");
|
|
3651
|
-
}
|
|
3652
|
-
const { platform, record } = deserialized;
|
|
3653
|
-
const runtime = platformStates.get(platform);
|
|
3654
|
-
if (!runtime) {
|
|
3655
|
-
lifecycleLog.warn(
|
|
3656
|
-
`spectrum.webhook: no provider configured for platform "${platform}"; acknowledging without delivery`,
|
|
3657
|
-
{ platform }
|
|
3658
|
-
);
|
|
3659
|
-
return webhookText(200, "ok");
|
|
3660
|
-
}
|
|
3661
|
-
deliverWebhookMessages([record], runtime, handler, { platform }).catch(
|
|
3662
|
-
(error) => {
|
|
3663
|
-
lifecycleLog.error(
|
|
3664
|
-
`spectrum.webhook: Spectrum delivery failed (async), ${error}`,
|
|
3665
|
-
{
|
|
3666
|
-
platform,
|
|
3667
|
-
messageId: record.id,
|
|
3668
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3669
|
-
}
|
|
3670
|
-
);
|
|
3671
|
-
}
|
|
3672
|
-
);
|
|
3673
|
-
return webhookText(200, "ok");
|
|
3674
|
-
};
|
|
3675
|
-
const handleWebhook = async (request, handler) => {
|
|
3676
|
-
const { asWeb, bodyBytes, headers } = await readWebhookInput(request);
|
|
3677
|
-
if (looksLikeNativePayload(bodyBytes)) {
|
|
3678
|
-
const spectrumResult = await handleSpectrumWebhook(
|
|
3679
|
-
bodyBytes,
|
|
3680
|
-
headers,
|
|
3681
|
-
handler
|
|
3682
|
-
);
|
|
3683
|
-
return buildWebhookResult(asWeb, spectrumResult);
|
|
3684
|
-
}
|
|
3685
|
-
if (!fusorCore) {
|
|
3686
|
-
throw new Error(
|
|
3687
|
-
"spectrum.webhook() received a non-Spectrum (fusor) request but no fusor provider is configured"
|
|
3688
|
-
);
|
|
3689
|
-
}
|
|
3690
|
-
const event = decodeWebhookEvent(bodyBytes);
|
|
3691
|
-
if (!event) {
|
|
3692
|
-
return buildWebhookResult(asWeb, {
|
|
3693
|
-
status: 400,
|
|
3694
|
-
headers: {},
|
|
3695
|
-
body: new Uint8Array(0)
|
|
3696
|
-
});
|
|
3697
|
-
}
|
|
3698
|
-
const result = await processWebhookEvent(fusorCore, event, handler);
|
|
3699
|
-
return buildWebhookResult(asWeb, result);
|
|
3700
|
-
};
|
|
3701
|
-
const base = {
|
|
3702
|
-
__providers: providers,
|
|
3703
|
-
__internal: { platforms: platformStates },
|
|
3704
|
-
config: projectConfig,
|
|
3705
|
-
messages,
|
|
3706
|
-
stop: stopOnce,
|
|
3707
|
-
webhook: handleWebhook,
|
|
3708
|
-
send: (async (space, ...content) => content.length === 1 ? await space.send(content[0]) : await space.send(
|
|
3709
|
-
...content
|
|
3710
|
-
)),
|
|
3711
|
-
edit: async (message, newContent) => {
|
|
3712
|
-
await message.edit(newContent);
|
|
3713
|
-
},
|
|
3714
|
-
responding: async (space, fn) => space.responding(fn)
|
|
3715
|
-
};
|
|
3716
|
-
return new Proxy(base, {
|
|
3717
|
-
get(target, prop, receiver) {
|
|
3718
|
-
if (prop in target) {
|
|
3719
|
-
return Reflect.get(target, prop, receiver);
|
|
3720
|
-
}
|
|
3721
|
-
if (typeof prop === "string") {
|
|
3722
|
-
return customEventProxy[prop];
|
|
3723
|
-
}
|
|
3724
|
-
return;
|
|
3725
|
-
}
|
|
3726
|
-
});
|
|
3727
|
-
}
|
|
3728
|
-
export {
|
|
3729
|
-
Emoji,
|
|
3730
|
-
Spectrum,
|
|
3731
|
-
SpectrumCloudError,
|
|
3732
|
-
UnsupportedError,
|
|
3733
|
-
attachment,
|
|
3734
|
-
avatar,
|
|
3735
|
-
broadcast,
|
|
3736
|
-
cloud,
|
|
3737
|
-
contact,
|
|
3738
|
-
custom,
|
|
3739
|
-
definePlatform,
|
|
3740
|
-
edit,
|
|
3741
|
-
fromVCard,
|
|
3742
|
-
fusor,
|
|
3743
|
-
fusorEvent,
|
|
3744
|
-
group,
|
|
3745
|
-
isFusorClient,
|
|
3746
|
-
isFusorEvent,
|
|
3747
|
-
markdown,
|
|
3748
|
-
mergeStreams,
|
|
3749
|
-
option,
|
|
3750
|
-
poll,
|
|
3751
|
-
reaction,
|
|
3752
|
-
read,
|
|
3753
|
-
rename,
|
|
3754
|
-
reply,
|
|
3755
|
-
resolveContents,
|
|
3756
|
-
richlink,
|
|
3757
|
-
stream,
|
|
3758
|
-
text,
|
|
3759
|
-
toVCard,
|
|
3760
|
-
typing,
|
|
3761
|
-
unsend,
|
|
3762
|
-
voice
|
|
3763
|
-
};
|
|
1
|
+
export * from "@spectrum-ts/core";
|
|
2
|
+
export {};
|