call_context_repository.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. from __future__ import annotations
  2. import json
  3. import sqlite3
  4. from datetime import datetime, timezone
  5. from pathlib import Path
  6. from typing import Any
  7. DB_PATH = Path(
  8. "/opt/3cx-middleware/3cx-telefonie-middleware/data/telephony.sqlite3"
  9. )
  10. class CallContextRepository:
  11. def __init__(self, db_path: str | Path = DB_PATH):
  12. self.db_path = str(db_path)
  13. self._ensure_schema()
  14. def _connect(self):
  15. con = sqlite3.connect(
  16. self.db_path,
  17. timeout=10,
  18. )
  19. con.row_factory = sqlite3.Row
  20. return con
  21. def _ensure_schema(self):
  22. with self._connect() as con:
  23. con.execute(
  24. """
  25. CREATE TABLE IF NOT EXISTS call_context (
  26. id INTEGER PRIMARY KEY AUTOINCREMENT,
  27. callid TEXT NOT NULL,
  28. legid TEXT,
  29. phone_e164 TEXT NOT NULL,
  30. context_json TEXT NOT NULL,
  31. created_at TEXT NOT NULL,
  32. updated_at TEXT NOT NULL,
  33. UNIQUE(callid, legid)
  34. )
  35. """
  36. )
  37. con.execute(
  38. """
  39. CREATE INDEX IF NOT EXISTS
  40. idx_call_context_phone
  41. ON call_context(phone_e164)
  42. """
  43. )
  44. con.commit()
  45. def upsert(
  46. self,
  47. *,
  48. callid: str,
  49. legid: str | None,
  50. phone_e164: str,
  51. context: dict[str, Any],
  52. ) -> None:
  53. now = datetime.now(
  54. timezone.utc
  55. ).isoformat()
  56. payload = json.dumps(
  57. context,
  58. ensure_ascii=False,
  59. separators=(",", ":"),
  60. default=str,
  61. )
  62. with self._connect() as con:
  63. con.execute(
  64. """
  65. INSERT INTO call_context (
  66. callid,
  67. legid,
  68. phone_e164,
  69. context_json,
  70. created_at,
  71. updated_at
  72. )
  73. VALUES (?, ?, ?, ?, ?, ?)
  74. ON CONFLICT(callid, legid)
  75. DO UPDATE SET
  76. phone_e164 = excluded.phone_e164,
  77. context_json = excluded.context_json,
  78. updated_at = excluded.updated_at
  79. """,
  80. (
  81. str(callid),
  82. str(legid) if legid is not None else None,
  83. phone_e164,
  84. payload,
  85. now,
  86. now,
  87. ),
  88. )
  89. con.commit()
  90. def get(
  91. self,
  92. *,
  93. callid: str,
  94. legid: str | None = None,
  95. ) -> dict[str, Any] | None:
  96. with self._connect() as con:
  97. if legid is None:
  98. row = con.execute(
  99. """
  100. SELECT *
  101. FROM call_context
  102. WHERE callid = ?
  103. ORDER BY id DESC
  104. LIMIT 1
  105. """,
  106. (str(callid),),
  107. ).fetchone()
  108. else:
  109. row = con.execute(
  110. """
  111. SELECT *
  112. FROM call_context
  113. WHERE callid = ?
  114. AND legid = ?
  115. LIMIT 1
  116. """,
  117. (str(callid), str(legid)),
  118. ).fetchone()
  119. if row is None:
  120. return None
  121. return {
  122. "callid": row["callid"],
  123. "legid": row["legid"],
  124. "phone_e164": row["phone_e164"],
  125. "context": json.loads(row["context_json"]),
  126. "created_at": row["created_at"],
  127. "updated_at": row["updated_at"],
  128. }