← All ivy-nodes
IVYXSTUDIO · IVY NODE
S
Social: pack the conversations
ivy.node.conversations-pack · v0.1.0
ivyx✓
Gathers the platform nodes' conversations into one answer of kind conversations, oldest first, each id once; what IVYX Social's inbox takes.
#social#inbox#ivyx-social
Inputs
| Field | Type | Description |
|---|---|---|
| client | string | Not declared |
| conversations | array | Conversations: id, account, kind, participant, post_key, messages. |
| conversations_a | array | Conversations: id, account, kind, participant, post_key, messages. |
| conversations_b | array | Conversations: id, account, kind, participant, post_key, messages. |
| conversations_c | array | Conversations: id, account, kind, participant, post_key, messages. |
| conversations_d | array | Conversations: id, account, kind, participant, post_key, messages. |
| conversations_e | array | Conversations: id, account, kind, participant, post_key, messages. |
Outputs
| Field | Type | Description |
|---|---|---|
| kindrequired | string | Always conversations. |
| conversations | array | Conversations: id, account, kind, participant, post_key, messages. |
| countrequired | integer | Not declared |
| client | string | Not declared |
Source
python
# Input preparation
inp = __ivy_ctx__["nodes"][__ivy_node_id__]["input"]
# Compute
# Shared by every social node (built into each by build.mjs): dates, blocks, merging.
import datetime, json, re, statistics
def day(s):
s = str(s or "")[:10]
return s if re.fullmatch(r"\d{4}-\d{2}-\d{2}", s) else None
def now_iso():
return datetime.datetime.utcnow().replace(microsecond=0).isoformat() + "Z"
def ms_of(s):
if s is None or s == "":
return None
if isinstance(s, (int, float)):
return int(s * 1000) if s < 1e12 else int(s)
t = str(s).replace("Z", "+00:00")
t = re.sub(r"([+-]\d{2})(\d{2})$", r"\1:\2", t)
try:
return int(datetime.datetime.fromisoformat(t).timestamp() * 1000)
except ValueError:
return None
def rows_of(block):
"""A {fields, rows} block as dicts."""
if not isinstance(block, dict) or not block.get("fields"):
return []
f = block["fields"]
return [dict(zip(f, r)) for r in block.get("rows") or []]
def block_of(dicts, first):
fields = list(first)
for d in dicts:
for k in d:
if k not in fields:
fields.append(k)
return {"fields": fields, "rows": [[d.get(k) for k in fields] for d in dicts]}
def listed(v):
return [x for x in (v if isinstance(v, list) else [v] if v else []) if x is not None]
def text_of(v, limit=4000):
s = " ".join(str(v).split()) if isinstance(v, (str, int, float)) else ""
return s[:limit]
class ConversationsPack:
"Social: pack the conversations"
def run(self, inp):
out = {}
# The platform nodes' conversations as one answer of kind conversations.
seen, convs = set(), []
for key in ("conversations", "conversations_a", "conversations_b", "conversations_c", "conversations_d", "conversations_e"):
for c in listed(inp.get(key)):
if isinstance(c, dict) and c.get("id") and c["id"] not in seen:
seen.add(c["id"]); convs.append(c)
convs.sort(key=lambda c: max((m.get("at") or 0) for m in c.get("messages") or [{}]) if c.get("messages") else 0)
out.update({"kind": "conversations", "conversations": convs, "count": len(convs), "client": inp.get("client") or ""})
return out
result = ConversationsPack().run(inp)
# Output collection (the runner reads it here)
out = __ivy_ctx__["nodes"][__ivy_node_id__]["output"]
out.update(result)Tests
Requires: python:3.9
- merge
Two nodes' conversations become one list, each id once, the oldest last message first.
- empty
No conversation from any node is an empty answer of the same kind.