Schema rewrite
This commit is contained in:
parent
3aea5c1696
commit
85648f4956
|
@ -1,224 +1,21 @@
|
||||||
version 1 .
|
version 1 .
|
||||||
|
|
||||||
AllowNo = <allow "no"> .
|
|
||||||
AllowYes = <allow "yes"> .
|
|
||||||
Allow = AllowNo / AllowYes .
|
|
||||||
UserAllow = <user Allow> .
|
|
||||||
|
|
||||||
EnableOffOn = EnableOff / EnableOn .
|
|
||||||
EnableOff = <enable "off"> .
|
|
||||||
EnableOn = <enable "on"> .
|
|
||||||
|
|
||||||
EnableNtfs = <enableNtfs bool> .
|
|
||||||
|
|
||||||
MIMEData = <mime @type symbol @data bytes> .
|
|
||||||
|
|
||||||
Contact = <contact @num int> .
|
|
||||||
|
|
||||||
Origin =
|
|
||||||
/ Contact
|
|
||||||
/ <user>
|
|
||||||
/ <unknown> .
|
|
||||||
|
|
||||||
Attributes = {symbol: any ...:...} .
|
Attributes = {symbol: any ...:...} .
|
||||||
|
|
||||||
Resp = <resp RespItem> .
|
MIMEData = <mime @type symbol @data bytes> .
|
||||||
|
|
||||||
RespItem =
|
ContactSubscription = { contact: Attributes } .
|
||||||
/ TimedAction
|
ContactSubscriptions2 = [ContactSubscription ...] .
|
||||||
/ ContactSubSummary
|
ContactSubscriptions1 = {
|
||||||
/ GroupSubscribed
|
contactSubscriptions: ContactSubscriptions2
|
||||||
/ MemberSubSummary
|
type: "contactSubSummary"
|
||||||
/ PendingSubSummary
|
} .
|
||||||
/ NewChatItem
|
ContactSubscriptions = { resp: ContactSubscriptions1 } .
|
||||||
/ Attributes .
|
|
||||||
|
|
||||||
TimedAction = <timedAction {
|
NewChatItem1 = {
|
||||||
action: string
|
chatInfo: Attributes
|
||||||
durationMilliseconds: int
|
chatItem: Attributes
|
||||||
}> .
|
type: "newChatItem"
|
||||||
|
|
||||||
ContactSubSummary = <contactSubSummary {
|
|
||||||
contactSubscriptions: [ContactSubscription ...]
|
|
||||||
user: User
|
|
||||||
}> .
|
|
||||||
|
|
||||||
ContactSubscription = <contact @attrs Attributes> .
|
|
||||||
|
|
||||||
ContactSubscriptionAttributes = {
|
|
||||||
activeConn: ActiveConn
|
|
||||||
chatSettings: EnableNtfs
|
|
||||||
chatTs: string
|
|
||||||
contactId: int
|
|
||||||
contactUsed: bool
|
|
||||||
createdAt: string
|
|
||||||
localDisplayName: string
|
|
||||||
mergedPreferences: MergedPreferences
|
|
||||||
profile: Profile
|
|
||||||
updatedAt: string
|
|
||||||
userPreferences: AllowPreferences
|
|
||||||
} .
|
} .
|
||||||
|
|
||||||
ActiveConn = {
|
NewChatItem = { resp: NewChatItem1 } .
|
||||||
agentConnId: string
|
|
||||||
authErrCounter: int
|
|
||||||
connId: int
|
|
||||||
connLevel: int
|
|
||||||
connStatus: string
|
|
||||||
connType: string
|
|
||||||
createdAt: string
|
|
||||||
entityId: int
|
|
||||||
localAlias: string
|
|
||||||
viaGroupLink: bool
|
|
||||||
} .
|
|
||||||
|
|
||||||
MergedPreferences = {
|
|
||||||
calls: ContactPreference
|
|
||||||
fullDelete: ContactPreference
|
|
||||||
reactions: ContactPreference
|
|
||||||
timedMessages: ContactPreference
|
|
||||||
voice: ContactPreference
|
|
||||||
} .
|
|
||||||
|
|
||||||
ContactPreference = {
|
|
||||||
contactPreference: Allow
|
|
||||||
enabled: ContactEnabled
|
|
||||||
userPreference: UserAllow
|
|
||||||
} .
|
|
||||||
|
|
||||||
ContactEnabled = {
|
|
||||||
forContact: bool
|
|
||||||
forUser: bool
|
|
||||||
} .
|
|
||||||
|
|
||||||
AllowPreferences = {string: Allow ...:...} .
|
|
||||||
|
|
||||||
GroupSubscribed = <groupSubscribed {
|
|
||||||
groupInfo: GroupInfo
|
|
||||||
user: LocalUser
|
|
||||||
}> .
|
|
||||||
|
|
||||||
GroupInfo = {
|
|
||||||
chatSettings: EnableNtfs
|
|
||||||
chatTs: "2023-07-22T11:54:25.14370346Z"
|
|
||||||
createdAt: "2023-07-22T11:52:01.935384979Z"
|
|
||||||
fullGroupPreferences: FullGroupPreferences
|
|
||||||
groupId: 6
|
|
||||||
groupProfile: Profile
|
|
||||||
hostConnCustomUserProfileId: any
|
|
||||||
localDisplayName: string
|
|
||||||
membership: MemberShip
|
|
||||||
updatedAt: string
|
|
||||||
} .
|
|
||||||
|
|
||||||
FullGroupPreferences = {
|
|
||||||
directMessages: EnableOffOn
|
|
||||||
fullDelete: EnableOffOn
|
|
||||||
reactions: EnableOffOn
|
|
||||||
timedMessages: TimedMessages
|
|
||||||
voice: EnableOffOn
|
|
||||||
} .
|
|
||||||
|
|
||||||
TimedMessages = {
|
|
||||||
enable: string ; "off"/"on"
|
|
||||||
ttl: int
|
|
||||||
} .
|
|
||||||
|
|
||||||
MemberShip = {
|
|
||||||
groupId: int
|
|
||||||
groupMemberId: int
|
|
||||||
invitedBy: Origin
|
|
||||||
localDisplayName: string
|
|
||||||
memberCategory: string
|
|
||||||
memberContactId: int
|
|
||||||
memberContactProfileId: int
|
|
||||||
memberId: string
|
|
||||||
memberProfile: Profile
|
|
||||||
memberRole: string
|
|
||||||
memberStatus: string
|
|
||||||
} .
|
|
||||||
|
|
||||||
Profile = {
|
|
||||||
displayName: string
|
|
||||||
fullName: string
|
|
||||||
} .
|
|
||||||
|
|
||||||
BigProfile = {
|
|
||||||
displayName: string
|
|
||||||
fullName: string
|
|
||||||
localAlias: string
|
|
||||||
profileId: int
|
|
||||||
}.
|
|
||||||
|
|
||||||
LargeProfile = {
|
|
||||||
displayName: string
|
|
||||||
fullName: string
|
|
||||||
image: MIMEData
|
|
||||||
localAlias: string
|
|
||||||
preferences: AllowPreferences
|
|
||||||
profileId: int
|
|
||||||
} .
|
|
||||||
|
|
||||||
LocalUser = {
|
|
||||||
activeUser: bool
|
|
||||||
agentUserId: string
|
|
||||||
fullPreferences: AllowPreferences
|
|
||||||
localDisplayName: string
|
|
||||||
profile: Profile
|
|
||||||
showNtfs: bool
|
|
||||||
userContactId: int
|
|
||||||
userId: int
|
|
||||||
} .
|
|
||||||
|
|
||||||
MemberSubSummary = <memberSubSummary {
|
|
||||||
memberSubscriptions: [MemberSubscription ...]
|
|
||||||
user: LocalUser
|
|
||||||
}> .
|
|
||||||
|
|
||||||
MemberSubscription = <member {
|
|
||||||
activeConn: ActiveConn
|
|
||||||
groupId: int
|
|
||||||
groupMemberId: int
|
|
||||||
invitedBy: Origin
|
|
||||||
localDisplayName: string
|
|
||||||
memberCategory: string
|
|
||||||
memberContactId: int
|
|
||||||
memberContactProfileId: int
|
|
||||||
memberId: string
|
|
||||||
memberProfile: Profile
|
|
||||||
memberRole: string
|
|
||||||
memberStatus: string
|
|
||||||
}> .
|
|
||||||
|
|
||||||
PendingSubSummary = <pendingSubSummary {
|
|
||||||
pendingSubscriptions: [Connection ...]
|
|
||||||
user: Attributes
|
|
||||||
}> .
|
|
||||||
|
|
||||||
Connection = <connection @attrs Attributes> .
|
|
||||||
|
|
||||||
NewChatItem = <newChatItem { chatItem: ChatItem0 }> .
|
|
||||||
|
|
||||||
ChatItem0 = {
|
|
||||||
chatInfo: ChatInfo
|
|
||||||
user: User
|
|
||||||
} .
|
|
||||||
|
|
||||||
ChatInfo = {
|
|
||||||
chatInfo: ChatInfo
|
|
||||||
chatItem: ChatItem1
|
|
||||||
} .
|
|
||||||
|
|
||||||
ChatItem1 = {
|
|
||||||
content: Content
|
|
||||||
} .
|
|
||||||
|
|
||||||
Content = Attributes .
|
|
||||||
|
|
||||||
User = {
|
|
||||||
activeUser: bool
|
|
||||||
agentUserId: string
|
|
||||||
localDisplayName: string
|
|
||||||
userContactId: int
|
|
||||||
userId: int
|
|
||||||
} .
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
version 1 .
|
||||||
|
embeddedType EntityRef.Cap .
|
||||||
|
|
||||||
|
ContactAssertion = <contact @id int @cap #!any> .
|
||||||
|
GroupAssertion = <group @id int @cap #!any> .
|
||||||
|
|
||||||
|
ReceivedMessage = <message @prevId any @msgId any @content Content> .
|
||||||
|
|
||||||
|
Content = <text @text string> .
|
|
@ -1,6 +1,6 @@
|
||||||
bin = @["simplex_bot_actor"]
|
bin = @["simplex_bot_actor"]
|
||||||
license = "Unlicense"
|
license = "Unlicense"
|
||||||
srcDir = "src"
|
srcDir = "src"
|
||||||
version = "20230724"
|
version = "20230726"
|
||||||
|
|
||||||
requires: "nim", "syndicate", ws
|
requires: "nim", "syndicate", ws
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
include_rules
|
include_rules
|
||||||
: foreach ../*.prs |> !preserves_schema_nim |> {schema}
|
: simplex_bot_actor.nim | $(SYNDICATE_PROTOCOL) ../<schema> |> !nim_bin |> {bin}
|
||||||
: simplex_bot_actor.nim | $(SYNDICATE_PROTOCOL) {schema} |> !nim_bin |> {bin}
|
|
||||||
: {bin} |> !assert_built |>
|
: {bin} |> !assert_built |>
|
||||||
|
|
|
@ -1,369 +0,0 @@
|
||||||
|
|
||||||
import
|
|
||||||
preserves, std/tables
|
|
||||||
|
|
||||||
type
|
|
||||||
ChatItem1* {.preservesDictionary.} = object
|
|
||||||
`content`*: Content
|
|
||||||
|
|
||||||
Resp* {.preservesRecord: "resp".} = ref object
|
|
||||||
`field0`*: RespItem
|
|
||||||
|
|
||||||
RespItemKind* {.pure.} = enum
|
|
||||||
`TimedAction`, `ContactSubSummary`, `GroupSubscribed`, `MemberSubSummary`,
|
|
||||||
`PendingSubSummary`, `NewChatItem`, `Attributes`
|
|
||||||
`RespItem`* {.preservesOr.} = ref object
|
|
||||||
case orKind*: RespItemKind
|
|
||||||
of RespItemKind.`TimedAction`:
|
|
||||||
`timedaction`*: TimedAction
|
|
||||||
|
|
||||||
of RespItemKind.`ContactSubSummary`:
|
|
||||||
`contactsubsummary`*: ContactSubSummary
|
|
||||||
|
|
||||||
of RespItemKind.`GroupSubscribed`:
|
|
||||||
`groupsubscribed`*: GroupSubscribed
|
|
||||||
|
|
||||||
of RespItemKind.`MemberSubSummary`:
|
|
||||||
`membersubsummary`*: MemberSubSummary
|
|
||||||
|
|
||||||
of RespItemKind.`PendingSubSummary`:
|
|
||||||
`pendingsubsummary`*: PendingSubSummary
|
|
||||||
|
|
||||||
of RespItemKind.`NewChatItem`:
|
|
||||||
`newchatitem`*: NewChatItem
|
|
||||||
|
|
||||||
of RespItemKind.`Attributes`:
|
|
||||||
`attributes`*: Attributes
|
|
||||||
|
|
||||||
|
|
||||||
AllowPreferences* = Table[string, Allow]
|
|
||||||
LocalUser* {.preservesDictionary.} = object
|
|
||||||
`activeUser`*: bool
|
|
||||||
`agentUserId`*: string
|
|
||||||
`fullPreferences`*: AllowPreferences
|
|
||||||
`localDisplayName`*: string
|
|
||||||
`profile`*: Profile
|
|
||||||
`showNtfs`*: bool
|
|
||||||
`userContactId`*: BiggestInt
|
|
||||||
`userId`*: BiggestInt
|
|
||||||
|
|
||||||
Attributes* = Table[Symbol, Preserve[void]]
|
|
||||||
MIMEData* {.preservesRecord: "mime".} = object
|
|
||||||
`type`*: Symbol
|
|
||||||
`data`*: seq[byte]
|
|
||||||
|
|
||||||
PendingSubSummaryField0* {.preservesDictionary.} = object
|
|
||||||
`pendingSubscriptions`*: seq[Connection]
|
|
||||||
`user`*: Attributes
|
|
||||||
|
|
||||||
PendingSubSummary* {.preservesRecord: "pendingSubSummary".} = object
|
|
||||||
`field0`*: PendingSubSummaryField0
|
|
||||||
|
|
||||||
ActiveConn* {.preservesDictionary.} = object
|
|
||||||
`agentConnId`*: string
|
|
||||||
`authErrCounter`*: BiggestInt
|
|
||||||
`connId`*: BiggestInt
|
|
||||||
`connLevel`*: BiggestInt
|
|
||||||
`connStatus`*: string
|
|
||||||
`connType`*: string
|
|
||||||
`createdAt`*: string
|
|
||||||
`entityId`*: BiggestInt
|
|
||||||
`localAlias`*: string
|
|
||||||
`viaGroupLink`*: bool
|
|
||||||
|
|
||||||
MemberSubSummaryField0* {.preservesDictionary.} = object
|
|
||||||
`memberSubscriptions`*: seq[MemberSubscription]
|
|
||||||
`user`*: LocalUser
|
|
||||||
|
|
||||||
MemberSubSummary* {.preservesRecord: "memberSubSummary".} = object
|
|
||||||
`field0`*: MemberSubSummaryField0
|
|
||||||
|
|
||||||
FullGroupPreferences* {.preservesDictionary.} = object
|
|
||||||
`directMessages`*: EnableOffOn
|
|
||||||
`fullDelete`*: EnableOffOn
|
|
||||||
`reactions`*: EnableOffOn
|
|
||||||
`timedMessages`*: TimedMessages
|
|
||||||
`voice`*: EnableOffOn
|
|
||||||
|
|
||||||
AllowNo* {.preservesRecord: "allow".} = object
|
|
||||||
`field0`* {.preservesLiteral: "\"no\"".}: tuple[]
|
|
||||||
|
|
||||||
TimedActionField0* {.preservesDictionary.} = object
|
|
||||||
`action`*: string
|
|
||||||
`durationMilliseconds`*: BiggestInt
|
|
||||||
|
|
||||||
TimedAction* {.preservesRecord: "timedAction".} = object
|
|
||||||
`field0`*: TimedActionField0
|
|
||||||
|
|
||||||
MemberSubscriptionField0* {.preservesDictionary.} = object
|
|
||||||
`activeConn`*: ActiveConn
|
|
||||||
`groupId`*: BiggestInt
|
|
||||||
`groupMemberId`*: BiggestInt
|
|
||||||
`invitedBy`*: Origin
|
|
||||||
`localDisplayName`*: string
|
|
||||||
`memberCategory`*: string
|
|
||||||
`memberContactId`*: BiggestInt
|
|
||||||
`memberContactProfileId`*: BiggestInt
|
|
||||||
`memberId`*: string
|
|
||||||
`memberProfile`*: Profile
|
|
||||||
`memberRole`*: string
|
|
||||||
`memberStatus`*: string
|
|
||||||
|
|
||||||
MemberSubscription* {.preservesRecord: "member".} = object
|
|
||||||
`field0`*: MemberSubscriptionField0
|
|
||||||
|
|
||||||
Contact* {.preservesRecord: "contact".} = object
|
|
||||||
`num`*: BiggestInt
|
|
||||||
|
|
||||||
UserAllow* {.preservesRecord: "user".} = object
|
|
||||||
`field0`*: Allow
|
|
||||||
|
|
||||||
ContactSubSummaryField0* {.preservesDictionary.} = object
|
|
||||||
`contactSubscriptions`*: seq[ContactSubscription]
|
|
||||||
`user`*: User
|
|
||||||
|
|
||||||
ContactSubSummary* {.preservesRecord: "contactSubSummary".} = object
|
|
||||||
`field0`*: ContactSubSummaryField0
|
|
||||||
|
|
||||||
EnableNtfs* {.preservesRecord: "enableNtfs".} = object
|
|
||||||
`field0`*: bool
|
|
||||||
|
|
||||||
EnableOff* {.preservesRecord: "enable".} = object
|
|
||||||
`field0`* {.preservesLiteral: "\"off\"".}: tuple[]
|
|
||||||
|
|
||||||
OriginKind* {.pure.} = enum
|
|
||||||
`Contact`, `user`, `unknown`
|
|
||||||
OriginUser* {.preservesRecord: "user".} = object
|
|
||||||
|
|
||||||
OriginUnknown* {.preservesRecord: "unknown".} = object
|
|
||||||
|
|
||||||
`Origin`* {.preservesOr.} = object
|
|
||||||
case orKind*: OriginKind
|
|
||||||
of OriginKind.`Contact`:
|
|
||||||
`contact`*: Contact
|
|
||||||
|
|
||||||
of OriginKind.`user`:
|
|
||||||
`user`*: OriginUser
|
|
||||||
|
|
||||||
of OriginKind.`unknown`:
|
|
||||||
`unknown`*: OriginUnknown
|
|
||||||
|
|
||||||
|
|
||||||
MergedPreferences* {.preservesDictionary.} = object
|
|
||||||
`calls`*: ContactPreference
|
|
||||||
`fullDelete`*: ContactPreference
|
|
||||||
`reactions`*: ContactPreference
|
|
||||||
`timedMessages`*: ContactPreference
|
|
||||||
`voice`*: ContactPreference
|
|
||||||
|
|
||||||
AllowKind* {.pure.} = enum
|
|
||||||
`AllowNo`, `AllowYes`
|
|
||||||
`Allow`* {.preservesOr.} = object
|
|
||||||
case orKind*: AllowKind
|
|
||||||
of AllowKind.`AllowNo`:
|
|
||||||
`allowno`*: AllowNo
|
|
||||||
|
|
||||||
of AllowKind.`AllowYes`:
|
|
||||||
`allowyes`*: AllowYes
|
|
||||||
|
|
||||||
|
|
||||||
NewChatItemField0* {.preservesDictionary.} = ref object
|
|
||||||
`chatItem`*: ChatItem0
|
|
||||||
|
|
||||||
NewChatItem* {.preservesRecord: "newChatItem".} = ref object
|
|
||||||
`field0`*: NewChatItemField0
|
|
||||||
|
|
||||||
EnableOn* {.preservesRecord: "enable".} = object
|
|
||||||
`field0`* {.preservesLiteral: "\"on\"".}: tuple[]
|
|
||||||
|
|
||||||
ChatInfo* {.preservesDictionary.} = ref object
|
|
||||||
`chatInfo`*: ChatInfo
|
|
||||||
`chatItem`*: ChatItem1
|
|
||||||
|
|
||||||
MemberShip* {.preservesDictionary.} = object
|
|
||||||
`groupId`*: BiggestInt
|
|
||||||
`groupMemberId`*: BiggestInt
|
|
||||||
`invitedBy`*: Origin
|
|
||||||
`localDisplayName`*: string
|
|
||||||
`memberCategory`*: string
|
|
||||||
`memberContactId`*: BiggestInt
|
|
||||||
`memberContactProfileId`*: BiggestInt
|
|
||||||
`memberId`*: string
|
|
||||||
`memberProfile`*: Profile
|
|
||||||
`memberRole`*: string
|
|
||||||
`memberStatus`*: string
|
|
||||||
|
|
||||||
TimedMessages* {.preservesDictionary.} = object
|
|
||||||
`enable`*: string
|
|
||||||
`ttl`*: BiggestInt
|
|
||||||
|
|
||||||
Profile* {.preservesDictionary.} = object
|
|
||||||
`displayName`*: string
|
|
||||||
`fullName`*: string
|
|
||||||
|
|
||||||
LargeProfile* {.preservesDictionary.} = object
|
|
||||||
`displayName`*: string
|
|
||||||
`fullName`*: string
|
|
||||||
`image`*: MIMEData
|
|
||||||
`localAlias`*: string
|
|
||||||
`preferences`*: AllowPreferences
|
|
||||||
`profileId`*: BiggestInt
|
|
||||||
|
|
||||||
ContactPreference* {.preservesDictionary.} = object
|
|
||||||
`contactPreference`*: Allow
|
|
||||||
`enabled`*: ContactEnabled
|
|
||||||
`userPreference`*: UserAllow
|
|
||||||
|
|
||||||
Content* = Attributes
|
|
||||||
ChatItem0* {.preservesDictionary.} = ref object
|
|
||||||
`chatInfo`*: ChatInfo
|
|
||||||
`user`*: User
|
|
||||||
|
|
||||||
EnableOffOnKind* {.pure.} = enum
|
|
||||||
`EnableOff`, `EnableOn`
|
|
||||||
`EnableOffOn`* {.preservesOr.} = object
|
|
||||||
case orKind*: EnableOffOnKind
|
|
||||||
of EnableOffOnKind.`EnableOff`:
|
|
||||||
`enableoff`*: EnableOff
|
|
||||||
|
|
||||||
of EnableOffOnKind.`EnableOn`:
|
|
||||||
`enableon`*: EnableOn
|
|
||||||
|
|
||||||
|
|
||||||
AllowYes* {.preservesRecord: "allow".} = object
|
|
||||||
`field0`* {.preservesLiteral: "\"yes\"".}: tuple[]
|
|
||||||
|
|
||||||
ContactEnabled* {.preservesDictionary.} = object
|
|
||||||
`forContact`*: bool
|
|
||||||
`forUser`*: bool
|
|
||||||
|
|
||||||
BigProfile* {.preservesDictionary.} = object
|
|
||||||
`displayName`*: string
|
|
||||||
`fullName`*: string
|
|
||||||
`localAlias`*: string
|
|
||||||
`profileId`*: BiggestInt
|
|
||||||
|
|
||||||
User* {.preservesDictionary.} = object
|
|
||||||
`activeUser`*: bool
|
|
||||||
`agentUserId`*: string
|
|
||||||
`localDisplayName`*: string
|
|
||||||
`userContactId`*: BiggestInt
|
|
||||||
`userId`*: BiggestInt
|
|
||||||
|
|
||||||
ContactSubscription* {.preservesRecord: "contact".} = object
|
|
||||||
`attrs`*: Attributes
|
|
||||||
|
|
||||||
ContactSubscriptionAttributes* {.preservesDictionary.} = object
|
|
||||||
`activeConn`*: ActiveConn
|
|
||||||
`chatSettings`*: EnableNtfs
|
|
||||||
`chatTs`*: string
|
|
||||||
`contactId`*: BiggestInt
|
|
||||||
`contactUsed`*: bool
|
|
||||||
`createdAt`*: string
|
|
||||||
`localDisplayName`*: string
|
|
||||||
`mergedPreferences`*: MergedPreferences
|
|
||||||
`profile`*: Profile
|
|
||||||
`updatedAt`*: string
|
|
||||||
`userPreferences`*: AllowPreferences
|
|
||||||
|
|
||||||
Connection* {.preservesRecord: "connection".} = object
|
|
||||||
`attrs`*: Attributes
|
|
||||||
|
|
||||||
GroupInfo* {.preservesDictionary.} = object
|
|
||||||
`chatSettings`*: EnableNtfs
|
|
||||||
`chatTs`* {.preservesLiteral: "\"2023-07-22T11:54:25.14370346Z\"".}: tuple[]
|
|
||||||
`createdAt`* {.preservesLiteral: "\"2023-07-22T11:52:01.935384979Z\"".}: tuple[]
|
|
||||||
`fullGroupPreferences`*: FullGroupPreferences
|
|
||||||
`groupId`* {.preservesLiteral: "6".}: tuple[]
|
|
||||||
`groupProfile`*: Profile
|
|
||||||
`hostConnCustomUserProfileId`*: Preserve[void]
|
|
||||||
`localDisplayName`*: string
|
|
||||||
`membership`*: MemberShip
|
|
||||||
`updatedAt`*: string
|
|
||||||
|
|
||||||
GroupSubscribedField0* {.preservesDictionary.} = object
|
|
||||||
`groupInfo`*: GroupInfo
|
|
||||||
`user`*: LocalUser
|
|
||||||
|
|
||||||
GroupSubscribed* {.preservesRecord: "groupSubscribed".} = object
|
|
||||||
`field0`*: GroupSubscribedField0
|
|
||||||
|
|
||||||
proc `$`*(x: ChatItem1 | Resp | RespItem | AllowPreferences | LocalUser |
|
|
||||||
Attributes |
|
|
||||||
MIMEData |
|
|
||||||
PendingSubSummary |
|
|
||||||
ActiveConn |
|
|
||||||
MemberSubSummary |
|
|
||||||
FullGroupPreferences |
|
|
||||||
AllowNo |
|
|
||||||
TimedAction |
|
|
||||||
MemberSubscription |
|
|
||||||
Contact |
|
|
||||||
UserAllow |
|
|
||||||
ContactSubSummary |
|
|
||||||
EnableNtfs |
|
|
||||||
EnableOff |
|
|
||||||
Origin |
|
|
||||||
MergedPreferences |
|
|
||||||
Allow |
|
|
||||||
NewChatItem |
|
|
||||||
EnableOn |
|
|
||||||
ChatInfo |
|
|
||||||
MemberShip |
|
|
||||||
TimedMessages |
|
|
||||||
Profile |
|
|
||||||
LargeProfile |
|
|
||||||
ContactPreference |
|
|
||||||
Content |
|
|
||||||
ChatItem0 |
|
|
||||||
EnableOffOn |
|
|
||||||
AllowYes |
|
|
||||||
ContactEnabled |
|
|
||||||
BigProfile |
|
|
||||||
User |
|
|
||||||
ContactSubscription |
|
|
||||||
ContactSubscriptionAttributes |
|
|
||||||
Connection |
|
|
||||||
GroupInfo |
|
|
||||||
GroupSubscribed): string =
|
|
||||||
`$`(toPreserve(x))
|
|
||||||
|
|
||||||
proc encode*(x: ChatItem1 | Resp | RespItem | AllowPreferences | LocalUser |
|
|
||||||
Attributes |
|
|
||||||
MIMEData |
|
|
||||||
PendingSubSummary |
|
|
||||||
ActiveConn |
|
|
||||||
MemberSubSummary |
|
|
||||||
FullGroupPreferences |
|
|
||||||
AllowNo |
|
|
||||||
TimedAction |
|
|
||||||
MemberSubscription |
|
|
||||||
Contact |
|
|
||||||
UserAllow |
|
|
||||||
ContactSubSummary |
|
|
||||||
EnableNtfs |
|
|
||||||
EnableOff |
|
|
||||||
Origin |
|
|
||||||
MergedPreferences |
|
|
||||||
Allow |
|
|
||||||
NewChatItem |
|
|
||||||
EnableOn |
|
|
||||||
ChatInfo |
|
|
||||||
MemberShip |
|
|
||||||
TimedMessages |
|
|
||||||
Profile |
|
|
||||||
LargeProfile |
|
|
||||||
ContactPreference |
|
|
||||||
Content |
|
|
||||||
ChatItem0 |
|
|
||||||
EnableOffOn |
|
|
||||||
AllowYes |
|
|
||||||
ContactEnabled |
|
|
||||||
BigProfile |
|
|
||||||
User |
|
|
||||||
ContactSubscription |
|
|
||||||
ContactSubscriptionAttributes |
|
|
||||||
Connection |
|
|
||||||
GroupInfo |
|
|
||||||
GroupSubscribed): seq[byte] =
|
|
||||||
encode(toPreserve(x))
|
|
|
@ -1,60 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
||||||
# SPDX-License-Identifier: Unlicense
|
|
||||||
|
|
||||||
import std/[base64, json, parseutils, tables]
|
|
||||||
import preserves
|
|
||||||
|
|
||||||
proc toPreserveHook*(js: JsonNode; E: typedesc): Preserve[E] {.gcsafe.} =
|
|
||||||
## Convert JSON to Preserves with some special rules to make
|
|
||||||
## traversing messages easier.
|
|
||||||
case js.kind
|
|
||||||
of JString:
|
|
||||||
var off = js.str.skip("data:")
|
|
||||||
if off == 0:
|
|
||||||
result = Preserve[E](kind: pkString, string: js.str)
|
|
||||||
else:
|
|
||||||
var mime: string
|
|
||||||
off.inc js.str.parseUntil(mime, ';', off)
|
|
||||||
var dataOff = off + js.str.skip(";base64,", off)
|
|
||||||
if dataOff != off:
|
|
||||||
var data = cast[seq[byte]](substr(js.str, dataOff).decode)
|
|
||||||
result = initRecord("mime", mime.toSymbol(E), data.toPreserve(E))
|
|
||||||
else:
|
|
||||||
result = Preserve[E](kind: pkString, string: js.str)
|
|
||||||
of JInt:
|
|
||||||
result = Preserve[E](kind: pkSignedInteger, int: js.num)
|
|
||||||
of JFloat:
|
|
||||||
result = Preserve[E](kind: pkDouble, double: js.fnum)
|
|
||||||
of JBool:
|
|
||||||
result = case js.bval
|
|
||||||
of false: toSymbol("false", E)
|
|
||||||
of true: toSymbol("true", E)
|
|
||||||
of JNull:
|
|
||||||
result = toSymbol("null", E)
|
|
||||||
of JArray:
|
|
||||||
result = Preserve[E](kind: pkSequence,
|
|
||||||
sequence: newSeq[Preserve[E]](js.elems.len))
|
|
||||||
for i, e in js.elems:
|
|
||||||
result.sequence[i] = toPreserveHook(e, E)
|
|
||||||
of JObject:
|
|
||||||
if js.hasKey("type"):
|
|
||||||
var label = js.getOrDefault("type").getStr.toSymbol(E)
|
|
||||||
if js.len == 1:
|
|
||||||
result = initRecord(label)
|
|
||||||
elif js.len == 2:
|
|
||||||
for key, val in js.fields.pairs:
|
|
||||||
if key != "type":
|
|
||||||
result = initRecord(label, val.toPreserve(E))
|
|
||||||
else:
|
|
||||||
var dict = Preserve[E](kind: pkDictionary)
|
|
||||||
for key, val in js.fields.pairs:
|
|
||||||
if key != "type":
|
|
||||||
dict[key.toSymbol(E)] = toPreserveHook(val, E)
|
|
||||||
result = initRecord(label, dict)
|
|
||||||
elif js.len == 1:
|
|
||||||
for key, val in js.fields.pairs:
|
|
||||||
result = initRecord(key, val.toPreserve(E))
|
|
||||||
else:
|
|
||||||
result = Preserve[E](kind: pkDictionary)
|
|
||||||
for key, val in js.fields.pairs:
|
|
||||||
result[key.toSymbol(E)] = toPreserveHook(val, E)
|
|
|
@ -1,12 +1,10 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[asyncdispatch, json, sequtils, streams, tables, uri]
|
import std/[base64, streams, tables]
|
||||||
import preserves, syndicate
|
import preserves, syndicate
|
||||||
import ws
|
|
||||||
|
|
||||||
import ./message_types
|
import ./simplex_bot_actor/[message_types, simple_types, websockets]
|
||||||
import ./private/jsonhooks
|
|
||||||
|
|
||||||
type
|
type
|
||||||
Value = Preserve[void]
|
Value = Preserve[void]
|
||||||
|
@ -15,42 +13,77 @@ type
|
||||||
dataspace: Cap
|
dataspace: Cap
|
||||||
url: string
|
url: string
|
||||||
|
|
||||||
|
ContactSubscription {.preservesDictionary.} = object
|
||||||
|
contact: Attributes
|
||||||
|
|
||||||
Internal* {.preservesRecord: "internal".} = object
|
Internal* {.preservesRecord: "internal".} = object
|
||||||
dataspace: Cap
|
dataspace: Cap
|
||||||
|
|
||||||
Contact = ref object
|
Contact = ref object
|
||||||
# TODO: does a Contact get its own facet
|
# TODO: does a Contact get its own facet
|
||||||
dataspace: Cap
|
dataspace: Cap
|
||||||
capHandle, subSummaryHandle, profileHandle: Handle
|
capHandle, summaryHandle, profileHandle, chatItemHandle: Handle
|
||||||
|
|
||||||
|
Group = ref object
|
||||||
|
dataspace: Cap
|
||||||
|
capHandle, infoHandle: Handle
|
||||||
|
|
||||||
|
# ContactAssertion = simple_types.ContactAssertion[Cap]
|
||||||
ContactAssertion {.preservesRecord: "contact".} = object
|
ContactAssertion {.preservesRecord: "contact".} = object
|
||||||
id: int
|
id: int
|
||||||
cap: Cap
|
cap: Cap
|
||||||
|
|
||||||
proc recvMessages(facet: Facet; ds: Cap; ws: WebSocket) =
|
GroupAssertion {.preservesRecord: "group".} = object
|
||||||
var fut: Future[string]
|
id: int
|
||||||
proc recvMessage() {.gcsafe.} =
|
cap: Cap
|
||||||
fut = receiveStrPacket ws
|
|
||||||
addCallback(fut, facet) do (turn: var Turn):
|
|
||||||
message(turn, ds, fut.read.parseJson)
|
|
||||||
recvMessage()
|
|
||||||
recvMessage()
|
|
||||||
|
|
||||||
proc bootContact(turn: var Turn; ds: Cap; id: int): Contact =
|
proc updateAttrs(contact: Contact; turn: var Turn; attrs: Attributes) =
|
||||||
result = Contact(dataspace: newDataspace(turn))
|
replace(turn, contact.dataspace, contact.summaryHandle, attrs)
|
||||||
result.capHandle = publish(turn, ds, ContactAssertion(id: id, cap: result.dataspace))
|
var profile = attrs.getOrDefault(Symbol"profile")
|
||||||
|
if not profile.isFalse:
|
||||||
|
replace(turn, contact.dataspace, contact.profileHandle, profile)
|
||||||
|
|
||||||
proc bootClient(turn: var Turn; ds: Cap; ws: WebSocket) =
|
proc updateAttrs(group: Group; turn: var Turn; info: Attributes) =
|
||||||
var contacts = initTable[int, Contact]()
|
replace(turn, group.dataspace, group.infoHandle, info)
|
||||||
|
|
||||||
|
proc `%`(bindings: sink openArray[(string, Pattern)]): Pattern =
|
||||||
|
## Sugar for creating dictionary patterns.
|
||||||
|
patterns.grabDictionary(bindings)
|
||||||
|
|
||||||
|
proc bootContact(turn: var Turn; intern: Cap; contactId: int): Contact =
|
||||||
|
let contact = Contact(dataspace: newDataspace(turn))
|
||||||
|
block:
|
||||||
|
let pat = grabRecord("recv", %{"resp": %{"contactUpdated": %{
|
||||||
|
"fromContact": %{"contactId": grab(contactId)},
|
||||||
|
"toContact": grab(),
|
||||||
|
}}})
|
||||||
|
onMessage(turn, intern, pat) do (attrs: Attributes):
|
||||||
|
updateAttrs(contact, turn, attrs)
|
||||||
|
|
||||||
|
block:
|
||||||
|
let pat = grabRecord("recv", %{"resp": %{"chatItem": %{
|
||||||
|
"chatInfo": %{"contact": %{"contactId": grab(contactId)}},
|
||||||
|
"chatItem": grab(),
|
||||||
|
}}}) # TODO: could update contact profiles from these messages
|
||||||
|
onMessage(turn, intern, pat) do (attrs: Attributes):
|
||||||
|
var
|
||||||
|
msgId = cast[seq[byte]](base64.decode(attrs[Symbol"meta"]["itemSharedMsgId".toSymbol].string))
|
||||||
|
msg = initRecord("message", Preserve[void](), msgId.toPreserve, attrs[Symbol"content"])
|
||||||
|
debugEcho "publish message ", msg
|
||||||
|
contact.chatItemHandle = publish(turn, contact.dataspace, msg)
|
||||||
|
|
||||||
|
contact
|
||||||
|
|
||||||
|
proc bootGroup(turn: var Turn; intern: Cap; groupId: int): Group =
|
||||||
|
let group = Group(dataspace: newDataspace(turn))
|
||||||
|
group
|
||||||
|
|
||||||
|
proc bootClient(turn: var Turn; ds, intern: Cap) =
|
||||||
|
var
|
||||||
|
contacts = initTable[int, Contact]()
|
||||||
|
groups = initTable[int, Group]()
|
||||||
# mapping of contactId to Contact data
|
# mapping of contactId to Contact data
|
||||||
|
|
||||||
let intern = newDataspace(turn)
|
|
||||||
discard publish(turn, ds, Internal(dataspace: intern))
|
|
||||||
# allocate a dataspace for internal messages and publish
|
|
||||||
|
|
||||||
recvMessages(turn.facet, intern, ws)
|
|
||||||
# start receiving loop
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let dumpStream = openFileStream("/tmp/simplex_bot_actor.log", fmWrite)
|
let dumpStream = openFileStream("/tmp/simplex_bot_actor.log", fmWrite)
|
||||||
onMessage(turn, intern, grab()) do (msg: Assertion):
|
onMessage(turn, intern, grab()) do (msg: Assertion):
|
||||||
|
@ -59,44 +92,46 @@ proc bootClient(turn: var Turn; ds: Cap; ws: WebSocket) =
|
||||||
write(dumpStream, '\n')
|
write(dumpStream, '\n')
|
||||||
flush(dumpStream)
|
flush(dumpStream)
|
||||||
|
|
||||||
block:
|
block: # concats
|
||||||
let pat = Resp ? { 0: (ContactSubSummary ? {
|
let pat = grabRecord("recv", %{"resp": %{
|
||||||
0: { "contactSubscriptions": grab() }.grabDictionary
|
"contactSubscriptions": grab(),
|
||||||
})
|
"type": grab"contactSubSummary",
|
||||||
}
|
}})
|
||||||
onMessage(turn, intern, pat) do (conSubs: seq[ContactSubscription]):
|
onMessage(turn, intern, pat) do (subs: seq[ContactSubscription]):
|
||||||
for conSub in conSubs:
|
for e in subs:
|
||||||
var id: int
|
var id: int
|
||||||
if id.fromPreserve(conSub.attrs[Symbol"contactId"]):
|
if id.fromPreserve(e.contact[Symbol"contactId"]):
|
||||||
var contact = contacts.getOrDefault(id)
|
var contact = contacts.getOrDefault(id)
|
||||||
if contact.isNil:
|
if contact.isNil:
|
||||||
contact = bootContact(turn, ds, id)
|
contact = bootContact(turn, intern, id)
|
||||||
contacts[id] = contact
|
contacts[id] = contact
|
||||||
replace(turn, contact.dataspace, contact.subSummaryHandle, conSub.attrs)
|
contact.capHandle = publish(turn, ds,
|
||||||
var profile = conSub.attrs.getOrDefault(Symbol"profile")
|
ContactAssertion(id: id, cap: contact.dataspace))
|
||||||
if not profile.isFalse:
|
updateAttrs(contact, turn, e.contact)
|
||||||
replace(turn, contact.dataspace, contact.profileHandle, profile)
|
|
||||||
|
|
||||||
proc boot*(root: Cap; turn: var Turn) =
|
block: # groups
|
||||||
during(turn, root, ?Args) do (ds: Cap, url: string):
|
let pat = grabRecord("recv", %{"resp": %{"groupInfo": grab()}})
|
||||||
|
onMessage(turn, intern, pat) do (info: Attributes):
|
||||||
|
var id: int
|
||||||
|
if id.fromPreserve(info[Symbol"groupId"]):
|
||||||
|
var group = groups.getOrDefault(id)
|
||||||
|
if group.isNil:
|
||||||
|
group = bootGroup(turn, intern, id)
|
||||||
|
groups[id] = group
|
||||||
|
group.capHandle = publish(turn, ds,
|
||||||
|
GroupAssertion(id: id, cap: group.dataspace))
|
||||||
|
updateAttrs(group, turn, info)
|
||||||
|
|
||||||
onPublish(turn, ds, ?ContactAssertion) do (id: int; cap: Cap):
|
onPublish(turn, ds, ?ContactAssertion) do (contactId: int; cap: Cap):
|
||||||
onPublish(turn, cap, { "localDisplayName": grab() }.grabDictionary) do (name: string):
|
onPublish(turn, cap, %{"localDisplayName": grab()}) do (name: string):
|
||||||
debugEcho "got contact ", name, " cap ", cap
|
onPublish(turn, cap, %{"image": ?MIMEData}) do (typ: Symbol, data: seq[byte]):
|
||||||
onPublish(turn, cap, { "image": ?MIMEData }.grabDictionary) do (typ: Symbol, data: seq[byte]):
|
debugEcho "contact ", name, " has an image of ", data.len, " bytes"
|
||||||
debugEcho "contact ", name, " has an image of ", data.len, " bytes"
|
|
||||||
|
|
||||||
debugEcho "got dataspace ", ds, " and URL ", url
|
runActor("eris_actor") do (root: Cap; turn: var Turn):
|
||||||
var ws: WebSocket
|
# connectStdio(root, turn)
|
||||||
newWebSocket(url).addCallback(turn) do (turn: var Turn; s: WebSocket):
|
spawnWebsocketJsonActor(turn, root)
|
||||||
ws = s
|
during(turn, root, ?Args) do (extern: Cap, url: string):
|
||||||
debugecho "connected to ", url
|
during(turn, root, JsonWebsocketAssertion ? { 0: ?url, 1: grab() }) do (intern: Cap):
|
||||||
bootClient(turn, ds, ws)
|
bootClient(turn, extern, intern)
|
||||||
do:
|
|
||||||
close ws
|
|
||||||
|
|
||||||
when isMainModule:
|
discard publish(turn, root, Args(dataspace: root, url: "ws://127.0.0.1:5225/"))
|
||||||
runActor("eris_actor") do (root: Cap; turn: var Turn):
|
|
||||||
# connectStdio(root, turn)
|
|
||||||
boot(root, turn)
|
|
||||||
discard publish(turn, root, Args(dataspace: root, url: "ws://127.0.0.1:5225/"))
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
include_rules
|
||||||
|
: foreach ../../*.prs |> !preserves_schema_nim |> | ../../<schema>
|
|
@ -0,0 +1,42 @@
|
||||||
|
|
||||||
|
import
|
||||||
|
preserves, std/tables
|
||||||
|
|
||||||
|
type
|
||||||
|
Attributes* = Table[Symbol, Preserve[void]]
|
||||||
|
MIMEData* {.preservesRecord: "mime".} = object
|
||||||
|
`type`*: Symbol
|
||||||
|
`data`*: seq[byte]
|
||||||
|
|
||||||
|
NewChatItem* {.preservesDictionary.} = object
|
||||||
|
`resp`*: NewChatItem1
|
||||||
|
|
||||||
|
ContactSubscriptions* {.preservesDictionary.} = object
|
||||||
|
`resp`*: ContactSubscriptions1
|
||||||
|
|
||||||
|
NewChatItem1* {.preservesDictionary.} = object
|
||||||
|
`chatInfo`*: Attributes
|
||||||
|
`chatItem`*: Attributes
|
||||||
|
`type`* {.preservesLiteral: "\"newChatItem\"".}: tuple[]
|
||||||
|
|
||||||
|
ContactSubscriptions1* {.preservesDictionary.} = object
|
||||||
|
`contactSubscriptions`*: ContactSubscriptions2
|
||||||
|
`type`* {.preservesLiteral: "\"contactSubSummary\"".}: tuple[]
|
||||||
|
|
||||||
|
ContactSubscriptions2* = seq[ContactSubscription]
|
||||||
|
ContactSubscription* {.preservesDictionary.} = object
|
||||||
|
`contact`*: Attributes
|
||||||
|
|
||||||
|
proc `$`*(x: Attributes | MIMEData | NewChatItem | ContactSubscriptions |
|
||||||
|
NewChatItem1 |
|
||||||
|
ContactSubscriptions1 |
|
||||||
|
ContactSubscriptions2 |
|
||||||
|
ContactSubscription): string =
|
||||||
|
`$`(toPreserve(x))
|
||||||
|
|
||||||
|
proc encode*(x: Attributes | MIMEData | NewChatItem | ContactSubscriptions |
|
||||||
|
NewChatItem1 |
|
||||||
|
ContactSubscriptions1 |
|
||||||
|
ContactSubscriptions2 |
|
||||||
|
ContactSubscription): seq[byte] =
|
||||||
|
encode(toPreserve(x))
|
|
@ -0,0 +1,27 @@
|
||||||
|
|
||||||
|
import
|
||||||
|
preserves
|
||||||
|
|
||||||
|
type
|
||||||
|
ContactAssertion* {.preservesRecord: "contact".} = object
|
||||||
|
`id`*: BiggestInt
|
||||||
|
`cap`* {.preservesEmbedded.}: Preserve[void]
|
||||||
|
|
||||||
|
ReceivedMessage* {.preservesRecord: "message".} = object
|
||||||
|
`prevId`*: Preserve[void]
|
||||||
|
`msgId`*: Preserve[void]
|
||||||
|
`content`*: Content
|
||||||
|
|
||||||
|
Content* {.preservesRecord: "text".} = object
|
||||||
|
`text`*: string
|
||||||
|
|
||||||
|
GroupAssertion* {.preservesRecord: "group".} = object
|
||||||
|
`id`*: BiggestInt
|
||||||
|
`cap`* {.preservesEmbedded.}: Preserve[void]
|
||||||
|
|
||||||
|
proc `$`*(x: ContactAssertion | ReceivedMessage | Content | GroupAssertion): string =
|
||||||
|
`$`(toPreserve(x))
|
||||||
|
|
||||||
|
proc encode*(x: ContactAssertion | ReceivedMessage | Content | GroupAssertion): seq[
|
||||||
|
byte] =
|
||||||
|
encode(toPreserve(x))
|
|
@ -0,0 +1,72 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import std/[asyncdispatch, json]
|
||||||
|
import preserves, preserves/jsonhooks
|
||||||
|
import syndicate, syndicate/actors
|
||||||
|
import ws
|
||||||
|
|
||||||
|
type JsonWebsocketAssertion* {.preservesRecord: "json-websocket".} = object
|
||||||
|
url: string
|
||||||
|
dataspace: Cap
|
||||||
|
|
||||||
|
type
|
||||||
|
SendJson* {.preservesRecord: "send".} = object
|
||||||
|
data: JsonNode
|
||||||
|
RecvJson* {.preservesRecord: "recv".} = object
|
||||||
|
data: JsonNode
|
||||||
|
|
||||||
|
proc spawnWebsocketJsonActor*(turn: var Turn; ds: Cap): Actor {.discardable.} =
|
||||||
|
## Spawn an actor that responds to observations of
|
||||||
|
## `<json-websocket @url string @dataspace #!Ref>`
|
||||||
|
## by connecting to Websocket urls and publishing dataspaces
|
||||||
|
## that carry messages to and from the Websocket endpoint.
|
||||||
|
spawn("json-websocket-actor", turn) do (turn: var Turn):
|
||||||
|
during(turn, ds, ?Observe(pattern: !JsonWebsocketAssertion) ?? {0: grabLit()}) do (url: string):
|
||||||
|
var ws: WebSocket
|
||||||
|
newWebSocket(url).addCallback(turn) do (turn: var Turn; sock: WebSocket):
|
||||||
|
ws = sock
|
||||||
|
let
|
||||||
|
facet = turn.facet
|
||||||
|
messageSpace = newDataspace(turn)
|
||||||
|
handle = publish(turn, ds,
|
||||||
|
JsonWebsocketAssertion(url: url, dataspace: messageSpace))
|
||||||
|
onStop(facet) do (turn: var Turn):
|
||||||
|
close(ws)
|
||||||
|
var fut: Future[(Opcode, string)]
|
||||||
|
proc recvMessage() {.gcsafe.} =
|
||||||
|
fut = receivePacket ws
|
||||||
|
addCallback(fut, facet) do (turn: var Turn):
|
||||||
|
let (opcode, data) = read fut
|
||||||
|
case opcode
|
||||||
|
of Text:
|
||||||
|
message(turn, messageSpace,
|
||||||
|
RecvJson(data: data.parseJson))
|
||||||
|
of Binary:
|
||||||
|
message(turn, messageSpace,
|
||||||
|
initRecord("recv", cast[seq[byte]](data).toPreserve))
|
||||||
|
of Ping:
|
||||||
|
asyncCheck(turn, ws.send(data, Pong))
|
||||||
|
of Pong, Cont:
|
||||||
|
discard
|
||||||
|
of Close:
|
||||||
|
retract(turn, handle)
|
||||||
|
stop(turn)
|
||||||
|
return
|
||||||
|
recvMessage()
|
||||||
|
recvMessage()
|
||||||
|
onMessage(turn, messageSpace, ?SendJson) do (data: JsonNode):
|
||||||
|
asyncCheck(turn, ws.send($data, Text))
|
||||||
|
do:
|
||||||
|
close(ws)
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
# Run as an independent component.
|
||||||
|
|
||||||
|
type Args {.preservesDictionary.} = object
|
||||||
|
dataspace: Cap
|
||||||
|
url: string
|
||||||
|
|
||||||
|
runActor("websocket-json-actor") do (root: Cap; turn: var Turn):
|
||||||
|
connectStdio(root, turn)
|
||||||
|
spawnWebsocketJsonActor(turn, root)
|
|
@ -0,0 +1,2 @@
|
||||||
|
include_rules
|
||||||
|
: foreach test*.nim | $(SYNDICATE_PROTOCOL) ../<protocol> |> !nim_run |>
|
|
@ -0,0 +1 @@
|
||||||
|
switch("path", "$projectDir/../src")
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue