chore: settle product naming across app, widget and deployments

One word everywhere: ReplyPier in UI copy, package and image names, the
replypier.site domains (app/api/docs/help), REPLYPIER_* settings and the
--rp-* design tokens.

- public widget API is window.ReplyPier with replypierId / replypierBaseUrl /
  replypierConfig / replypierToken, loader at webclient/replypier.min.js;
  both bundles rebuilt from source
- NOTICE, llms.txt, CONTRIBUTING, HELP_CENTER_INFRA and the demo runtime
  config rewritten; the demo config ships no third-party keys
- migration adds the ReplyPier managed model value to the AI model enum
- local compose project, database, role, volume and Docker network carry the
  same name
This commit is contained in:
WangShen007 committed 2026-10-26 00:57:22 +08:00
1 parent ff0a09f0e5
commit 20fef2b9f3
1156 files changed
+7267 -7148

No files matched your search

+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+37 -37
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -153,7 +153,7 @@ def mock_validate_model_selection(model_type: str, model_name: str):
detail={
"error": "Unsupported provider",
"type": "invalid_provider",
"details": "Currently only these providers are supported: GROQ, OPENAI, CHATTERMATE"
"details": "Currently only these providers are supported: GROQ, OPENAI, REPLYPIER"
}
)
return original_validate(model_type, model_name)
@@ -318,16 +318,16 @@ def test_setup_ai_groq(client, db, test_user):
assert data["config"]["model_name"] == "llama-3.3-70b-versatile"
@patch.dict(os.environ, {
'CHATTERMATE_API_KEY': 'test_chattermate_key',
'CHATTERMATE_MODEL_NAME': 'gpt-4o-mini'
'REPLYPIER_API_KEY': 'test_replypier_key',
'REPLYPIER_MODEL_NAME': 'gpt-4o-mini'
})
@patch('app.api.ai_setup.HAS_ENTERPRISE', True)
def test_setup_ai_chattermate_success(client, db, test_user):
"""Test successful ChatterMate AI setup"""
def test_setup_ai_replypier_success(client, db, test_user):
"""Test successful ReplyPier AI setup"""
config_data = {
"model_type": "CHATTERMATE",
"model_name": "chattermate",
"api_key": "any_key" # This will be ignored for ChatterMate
"model_type": "REPLYPIER",
"model_name": "replypier",
"api_key": "any_key" # This will be ignored for ReplyPier
}
response = client.post(
@@ -337,16 +337,16 @@ def test_setup_ai_chattermate_success(client, db, test_user):
assert response.status_code == 200
data = response.json()
assert data["message"] == "AI configuration completed successfully"
assert data["config"]["model_type"] == "CHATTERMATE"
assert data["config"]["model_type"] == "REPLYPIER"
assert data["config"]["model_name"] == "gpt-4o-mini"
@patch.dict(os.environ, {}, clear=True) # Clear environment variables
@patch('app.api.ai_setup.HAS_ENTERPRISE', True)
def test_setup_ai_chattermate_missing_key(client, db, test_user):
"""Test ChatterMate AI setup with missing API key"""
def test_setup_ai_replypier_missing_key(client, db, test_user):
"""Test ReplyPier AI setup with missing API key"""
config_data = {
"model_type": "CHATTERMATE",
"model_name": "chattermate",
"model_type": "REPLYPIER",
"model_name": "replypier",
"api_key": "any_key"
}
@@ -355,7 +355,7 @@ def test_setup_ai_chattermate_missing_key(client, db, test_user):
json=config_data
)
assert response.status_code == 500
assert "ChatterMate API configuration missing" in response.json()["detail"]
assert "ReplyPier API configuration missing" in response.json()["detail"]
def test_setup_ai_general_exception(client, db, test_user):
"""Test setup AI with general exception"""
@@ -432,8 +432,8 @@ def test_get_providers(client, db, test_user):
# Newly enabled providers are present alongside OpenAI/Groq
for expected in ("OPENAI", "GROQ", "ANTHROPIC", "GOOGLE", "MISTRAL", "XAI", "DEEPSEEK", "OPENAI_COMPATIBLE"):
assert expected in providers, f"{expected} missing from /providers"
# ChatterMate (managed) is not a user-selectable BYO-key provider
assert "CHATTERMATE" not in providers
# ReplyPier (managed) is not a user-selectable BYO-key provider
assert "REPLYPIER" not in providers
# Each provider exposes suggested models and BYO-key metadata
openai = providers["OPENAI"]
assert openai["requires_api_key"] is True
@@ -616,15 +616,15 @@ def test_update_ai_config_validation_exception(client, db, test_user, test_ai_co
assert data["detail"]["type"] == "api_key_validation_error"
@patch.dict(os.environ, {
'CHATTERMATE_API_KEY': 'test_chattermate_key',
'CHATTERMATE_MODEL_NAME': 'gpt-4o-mini'
'REPLYPIER_API_KEY': 'test_replypier_key',
'REPLYPIER_MODEL_NAME': 'gpt-4o-mini'
})
@patch('app.api.ai_setup.HAS_ENTERPRISE', True)
def test_update_ai_config_chattermate_success(client, db, test_user, test_ai_config):
"""Test successful ChatterMate AI config update"""
def test_update_ai_config_replypier_success(client, db, test_user, test_ai_config):
"""Test successful ReplyPier AI config update"""
update_data = {
"model_type": "CHATTERMATE",
"model_name": "chattermate"
"model_type": "REPLYPIER",
"model_name": "replypier"
}
response = client.put(
@@ -634,15 +634,15 @@ def test_update_ai_config_chattermate_success(client, db, test_user, test_ai_con
assert response.status_code == 200
data = response.json()
assert data["message"] == "AI configuration updated successfully"
assert data["config"]["model_type"] == "CHATTERMATE"
assert data["config"]["model_type"] == "REPLYPIER"
@patch.dict(os.environ, {}, clear=True)
@patch('app.api.ai_setup.HAS_ENTERPRISE', True)
def test_update_ai_config_chattermate_missing_key(client, db, test_user, test_ai_config):
"""Test ChatterMate AI config update with missing API key"""
def test_update_ai_config_replypier_missing_key(client, db, test_user, test_ai_config):
"""Test ReplyPier AI config update with missing API key"""
update_data = {
"model_type": "CHATTERMATE",
"model_name": "chattermate"
"model_type": "REPLYPIER",
"model_name": "replypier"
}
response = client.put(
@@ -650,7 +650,7 @@ def test_update_ai_config_chattermate_missing_key(client, db, test_user, test_ai
json=update_data
)
assert response.status_code == 500
assert "ChatterMate API configuration missing" in response.json()["detail"]
assert "ReplyPier API configuration missing" in response.json()["detail"]
def test_update_ai_config_general_exception(client, db, test_user, test_ai_config):
"""Test update AI config with general exception"""
@@ -670,11 +670,11 @@ def test_update_ai_config_general_exception(client, db, test_user, test_ai_confi
assert response.status_code == 500
assert response.json()["detail"] == "Failed to update AI configuration"
def test_validate_model_selection_chattermate():
"""Test validate_model_selection with ChatterMate"""
def test_validate_model_selection_replypier():
"""Test validate_model_selection with ReplyPier"""
# Should not raise any exception
ai_setup_router.validate_model_selection("CHATTERMATE", "chattermate")
ai_setup_router.validate_model_selection("chattermate", "CHATTERMATE")
ai_setup_router.validate_model_selection("REPLYPIER", "replypier")
ai_setup_router.validate_model_selection("replypier", "REPLYPIER")
def test_validate_model_selection_groq_valid():
"""Test validate_model_selection with valid Groq model"""
@@ -726,18 +726,18 @@ def test_validate_model_selection_unsupported_provider():
def test_setup_ai_unsupported_model_type(client, db, test_user):
"""Test setup AI with unsupported model type for API key validation"""
config_data = {
"model_type": "CHATTERMATE",
"model_name": "some-model", # Invalid model name for ChatterMate
"model_type": "REPLYPIER",
"model_name": "some-model", # Invalid model name for ReplyPier
"api_key": "test_valid_key"
}
# When HAS_ENTERPRISE is False, ChatterMate should be rejected by validation
# When HAS_ENTERPRISE is False, ReplyPier should be rejected by validation
with patch('app.api.ai_setup.HAS_ENTERPRISE', False):
response = client.post(
"/api/ai/setup",
json=config_data
)
# Should fail validation because CHATTERMATE with non-"chattermate" model name is invalid
# Should fail validation because REPLYPIER with non-"replypier" model name is invalid
assert response.status_code == 400
data = response.json()
assert "error" in data["detail"]
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+3 -3
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -152,7 +152,7 @@ def test_faq_seo_fields_round_trip(client):
created = client.post(f"{BASE}/faqs", json={
"question": "What does it cost?", "answer": "From $19.",
"slug": "Pricing & Plans!", "meta_title": "Pricing",
"meta_description": "What ChatterMate costs.",
"meta_description": "What ReplyPier costs.",
})
assert created.status_code == 201
# A hand-typed slug is normalized the same way a generated one is.
@@ -163,7 +163,7 @@ def test_faq_seo_fields_round_trip(client):
updated = client.put(f"{BASE}/faqs/{faq_id}", json={"slug": "our-pricing"})
assert updated.json()["slug"] == "our-pricing"
# Untouched overrides survive a partial update.
assert updated.json()["meta_description"] == "What ChatterMate costs."
assert updated.json()["meta_description"] == "What ReplyPier costs."
def test_faq_slug_is_deduped_but_stable_for_its_own_row(client):
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+15 -15
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ from app.models.help_center import HelpCenterSettings
from app.repositories.faq import FAQRepository
from app.services.help_center_public import contrast_ink, resolve_help_center, slug_for_host
HOST = "test-org.chattermate.help"
HOST = "test-org.help.replypier.site"
@pytest.fixture(autouse=True)
@@ -108,9 +108,9 @@ def path_client(db, monkeypatch):
# ---------- helpers ----------
def test_slug_for_host():
assert slug_for_host("acme.chattermate.help") == "acme"
assert slug_for_host("a.b.chattermate.help") is None
assert slug_for_host("chattermate.help") is None
assert slug_for_host("acme.help.replypier.site") == "acme"
assert slug_for_host("a.b.help.replypier.site") is None
assert slug_for_host("help.replypier.site") is None
assert slug_for_host("help.acme.com") is None
@@ -279,7 +279,7 @@ def test_widget_embed_points_at_this_install(
client, db, test_organization, help_center, test_widget, monkeypatch
):
"""The loader falls back to the API URL baked in at build time (the vendor
cloud) unless the page sets window.chattermateBaseUrl. Without it, a
cloud) unless the page sets window.replypierBaseUrl. Without it, a
self-hosted help center asks the wrong backend for its widget and the
visitor gets "Chat Unavailable"."""
monkeypatch.setattr(settings, "BACKEND_URL", "https://api.selfhosted.example")
@@ -287,8 +287,8 @@ def test_widget_embed_points_at_this_install(
db.commit()
r = client.get("/", headers={"host": HOST})
assert f'window.chattermateId = "{test_widget.id}"' in r.text
assert 'window.chattermateBaseUrl = "https://api.selfhosted.example/api/v1"' in r.text
assert f'window.replypierId = "{test_widget.id}"' in r.text
assert 'window.replypierBaseUrl = "https://api.selfhosted.example/api/v1"' in r.text
def test_custom_domain_page_is_self_contained(
@@ -316,7 +316,7 @@ def test_custom_domain_page_is_self_contained(
'<meta property="og:image" content="https://help.customer.com/api/v1/uploads/help_center/logo.png">'
in r.text
)
assert 'window.chattermateBaseUrl = "https://api.selfhosted.example/api/v1"' in r.text
assert 'window.replypierBaseUrl = "https://api.selfhosted.example/api/v1"' in r.text
def test_og_card_degrades_without_a_logo(client, db, test_organization, help_center):
@@ -336,7 +336,7 @@ def test_search_filters_results(client, db, test_organization, help_center):
def test_disabled_or_unknown_hosts_404(client, db, test_organization, help_center):
assert client.get("/", headers={"host": "nope.chattermate.help"}).status_code == 404
assert client.get("/", headers={"host": "nope.help.replypier.site"}).status_code == 404
help_center.enabled = False
db.commit()
assert client.get("/", headers={"host": HOST}).status_code == 404
@@ -480,9 +480,9 @@ def test_ask_unanswerable_503(client, db, test_organization, help_center, test_a
# ---------- host dispatch ----------
def test_is_help_center_host_matches_subdomains_only():
assert is_help_center_host("acme.chattermate.help") is True
assert is_help_center_host("chattermate.help") is False
assert is_help_center_host("api.chattermate.chat") is False
assert is_help_center_host("acme.help.replypier.site") is True
assert is_help_center_host("help.replypier.site") is False
assert is_help_center_host("api.replypier.site") is False
assert is_help_center_host("") is False
@@ -566,7 +566,7 @@ def test_preserved_path_drives_canonical_and_json_ld(
r = client.get(MIGRATED_PATH, headers={"host": HOST})
expected = f"https://test-org.chattermate.help{MIGRATED_PATH}"
expected = f"https://test-org.help.replypier.site{MIGRATED_PATH}"
assert f'<link rel="canonical" href="{expected}">' in r.text
assert f'<meta property="og:url" content="{expected}">' in r.text
graph = json.loads(re.search(
@@ -583,7 +583,7 @@ def test_sitemap_lists_the_preserved_path(client, db, test_organization, help_ce
r = client.get("/sitemap.xml", headers={"host": HOST})
assert f"<loc>https://test-org.chattermate.help{MIGRATED_PATH}</loc>" in r.text
assert f"<loc>https://test-org.help.replypier.site{MIGRATED_PATH}</loc>" in r.text
assert "/a/how-do-i-reset-my-password" not in r.text
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@ ASK = "/health/help-center-domain"
def test_allows_slug_subdomain():
# {slug}.HELP_CENTER_BASE_DOMAIN is a help-center host without any DB lookup.
assert client.get(ASK, params={"domain": "acme.chattermate.help"}).status_code == 200
assert client.get(ASK, params={"domain": "acme.help.replypier.site"}).status_code == 200
def test_allows_verified_custom_domain():
@@ -53,4 +53,4 @@ def test_rejects_missing_or_empty_domain():
def test_normalizes_host_with_port_and_uppercase():
# SNI can arrive with a port / mixed case; normalize_host must canonicalize
# it so the slug match still succeeds.
assert client.get(ASK, params={"domain": "ACME.chattermate.help:443"}).status_code == 200
assert client.get(ASK, params={"domain": "ACME.help.replypier.site:443"}).status_code == 200
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+2 -2
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@ limitations under the License.
"""
"""
ChatterMate - MCP Tool API Tests
ReplyPier - MCP Tool API Tests
"""
import pytest
+2 -2
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -426,7 +426,7 @@ class TestTemplateManagement:
assert graph.await_count == 1
class TestEmbeddedSignupConfig:
"""Embedded Signup onboards a number under ChatterMate's own approved Meta
"""Embedded Signup onboards a number under ReplyPier's own approved Meta
app, so without a config id it structurally cannot work — that is the whole
gate. Like every other integration, it is not restricted by plan."""
+8 -8
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -40,8 +40,8 @@ from app.repositories.channels import ChannelAccountRepository
BASE = "/api/v1/channels/meta"
WEBHOOK = "/api/v1/webhooks/meta"
OUR_SECRET = "chattermate-app-secret"
OUR_APP_ID = "APP_CHATTERMATE"
OUR_SECRET = "replypier-app-secret"
OUR_APP_ID = "APP_REPLYPIER"
THEIR_SECRET = "customer-app-secret"
THEIR_APP_ID = "APP_THEIRS"
OVERRIDE_RELEASED = {"webhook_configuration": {"override_callback_uri": ""}}
@@ -133,7 +133,7 @@ class TestOwnAppWebhook:
@pytest.mark.parametrize("secret", [OUR_SECRET, "guess"])
def test_any_other_secret_is_refused(self, client, db, test_organization, secret):
"""ChatterMate's own secret included: this address answers to one app only."""
"""ReplyPier's own secret included: this address answers to one app only."""
account = _account(db, test_organization.id)
response, process, _ = _post(client, account.id, _wa_message(), secret)
assert response.status_code == 403
@@ -163,7 +163,7 @@ class TestOwnAppWebhook:
db.commit()
_account(db, other_org.id, external_id="PN-THEIRS") # same app id, other org
_account(db, test_organization.id, external_id="PN-OTHER-APP", app_id="APP_ELSE")
_account(db, test_organization.id, external_id="PN-SHARED", app_id=None) # via ChatterMate's app
_account(db, test_organization.id, external_id="PN-SHARED", app_id=None) # via ReplyPier's app
for number in ("PN-THEIRS", "PN-OTHER-APP", "PN-SHARED"):
response, process, status = _post(client, ours.id, _wa_message(number))
assert response.status_code == 200
@@ -208,7 +208,7 @@ class TestOwnAppWebhook:
"hub.mode": "subscribe", "hub.verify_token": token, "hub.challenge": "42"})
assert bad.status_code == 403
def test_no_handshake_for_an_account_from_chattermates_app(self, client, db, test_organization):
def test_no_handshake_for_an_account_from_replypiers_app(self, client, db, test_organization):
account = _account(db, test_organization.id, app_id=None)
response = client.get(f"{WEBHOOK}/{account.id}", params={
"hub.mode": "subscribe", "hub.verify_token": account.webhook_secret, "hub.challenge": "42"})
@@ -311,8 +311,8 @@ class TestConnect:
assert response.json()["webhook_url"] is None
assert APP_ID_SETTING not in (_stored(db)[0].settings or {})
def test_chattermates_own_app_needs_no_address(self, client, db):
"""A self-hoster's number in the app ChatterMate runs on already
def test_replypiers_own_app_needs_no_address(self, client, db):
"""A self-hoster's number in the app ReplyPier runs on already
reaches the shared address: nothing is stored or re-pointed."""
response, _, graph_post = _connect_whatsapp(client, app=(True, {"id": OUR_APP_ID}))
assert response.status_code == 200
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
"""
Copyright 2024-2026 ChatterMate
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.