=== NeccoSpeak – AI Chatbot & Voice Chat ===
Contributors: neccos
Tags: ai chatbot, voice chat, chatbot, openai, ai
Requires at least: 6.4
Tested up to: 7.1
Requires PHP: 8.0
Stable tag: 0.3.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Let visitors talk to your website — an AI agent with text chat and real-time voice, on your own OpenAI key. No SaaS, no extra account, no monthly fee.

== Description ==

NeccoSpeak turns your WordPress site into something visitors can simply talk to: an AI agent that answers questions 24/7 by text or voice, guides people to the right page, and runs entirely on your own OpenAI API key.

No SaaS, no extra account, no monthly fee, and no server run by us. It needs nothing but WordPress and your own OpenAI API key. You pay OpenAI directly and stay in control of your costs and your data.

= Voice chat: talk, don't type =

AI voice chat sets NeccoSpeak apart, and it is on by default. Visitors ask questions hands-free and hear the answer spoken back — in effect, your website gains a voice assistant — a natural fit for accessibility, mobile users, and anyone who would rather speak than type. Under the hood, the visitor's browser connects directly to the OpenAI Realtime API over WebRTC using a short-lived token issued by your server; your real API key never reaches the browser. Voice requires HTTPS and browser microphone permission.

= What can you use it for? =

* **24/7 FAQ and support** – answer common questions (opening hours, pricing, services) around the clock.
* **Online stores** – let shoppers ask about shipping, returns, and store policies on the spot, instead of hunting through pages.
* **Multilingual visitors** – OpenAI models typically reply in the language the visitor writes in, with no extra configuration.
* **Blogs and content sites** – give readers a way to ask questions instead of leaving.
* **Client sites** – agencies and freelancers can deploy it without managing yet another SaaS account; each client brings their own key and owns their own billing and data.

You decide who the chatbot is: describe your business, opening hours, policies, and tone in the system prompt, and it answers accordingly.

= Why NeccoSpeak? =

* **Self-contained** – no third-party SaaS involved; data flows only between your server, the visitor's browser, and OpenAI, unless you reference an external URL in the optional Additional CSS box (see "External Services" below).
* **Secure by design** – your API key is stored encrypted with AES-256-CBC, never sent to the browser, and masked in the admin screen.
* **Zero footprint until configured** – while disabled or without a key, the widget is not loaded and visitors trigger no external communication at all. (This does not apply if the Additional CSS box references an external image/font URL — see "External Services".)
* **Budget protection** – per-IP rate limits plus site-wide caps (hourly and daily for chat, daily for voice) keep bots and abusive visitors from running up your OpenAI bill.
* **No update needed for new models** – type any model name, or click once to fetch the list of models available to your key. New OpenAI models that support the Chat Completions API work as soon as they are released.

= Features =

* Text and voice chat in one widget
* Display rules: all pages, only specific URLs, or exclude specific URLs
* Customizable title, welcome message, and brand color
* Free-form model selection with one-click "fetch available models"
* Custom system prompt
* Per-IP and site-wide rate limits to control cost

= Cost control =

Visitors' chat usage is billed to your own OpenAI account, so NeccoSpeak ships with brakes built in: a per-IP, per-minute limit plus site-wide caps (hourly and daily for text chat, daily for voice sessions). When a cap is hit, further requests are declined with a temporary error until the limit window resets — so your bill stops growing.
Defaults: chat 10/min per IP plus 200/hour and 1,000/day site-wide; voice 3/min per IP plus 200/day site-wide.

= Content sync (optional) =

The Content sync card on the AI settings tab lets you choose how much of your own site the assistant can work with.

* **Per-page instructions** (on by default) – adds a "NeccoSpeak" box to every post/page editor where you can set extra instructions, a welcome-message override, and a widget show/hide switch for that one page.
* **Read-only tools** (on by default) – let the assistant search this site and look up page details. Each individual tool also has to be enabled with its own checkbox before it runs.
* **Page directory** (optional) – gives the assistant a list of your published pages so it can point visitors to the right one. Links the assistant produces are checked against your own site, and links to pages that do not exist are removed automatically.
* **Current date and time** – always supplied to the assistant in your site's own timezone, with no setting needed.
* **Site content search** (optional) – stores excerpts of your posts and pages in the plugin's own database table so the assistant can quote your content directly. No external service is used for the indexing or the search.

The two optional features (page directory and site content search) stay off until you turn them on, and none of these features contact any additional external service.

= Terms and disclaimer =

* **No warranty** – This plugin is provided free of charge as open source software (GPLv2 or later) on an "as-is" basis, without warranty of any kind, express or implied. Because it is distributed free of charge, the developer (neccos) assumes no obligation for support, SLA, or continued availability.
* **Your OpenAI account** – The plugin runs with your own OpenAI API key, and use of the AI is subject to OpenAI's terms and policies. Usage fees are billed by OpenAI directly to your own account. No developer-operated server or service exists, and the developer is not involved in any contract, billing, or service matters with OpenAI.
* **Data flow** – Chat text and audio are sent to OpenAI with your key and processed under OpenAI's terms. **They are sent only to OpenAI, and the developer (neccos) never receives this data.** Everything the plugin stores within WordPress is administrator-entered settings (the API key is encrypted) plus operational data such as rate-limit counters and the audit log, all removed on uninstall (see "Data stored on your site" below for details).
* **Your responsibilities** – As the site owner, you are responsible for: (1) complying with OpenAI's terms, (2) disclosing to your visitors that their input is sent to OpenAI and obtaining any consent required by applicable law such as GDPR, and (3) maintaining your own privacy policy and related documents. Please also advise users not to enter sensitive information in chat or voice.
* **AI responses** – The accuracy, legality, and usefulness of AI-generated responses are not guaranteed. Use of this plugin and of AI responses is at the sole risk of the site owner and end users.

== Installation ==

1. Install the plugin from "Add New" in the admin Plugins screen (or upload it), then activate it.
2. Open the top-level **NeccoSpeak** menu in the admin sidebar and paste your own **OpenAI API key**. It is stored encrypted and masked thereafter.
3. Optionally set the model, system prompt, welcome message, brand color, display rules, and rate limits.
4. That's it — once enabled with a key set, the chat widget is live on the pages your display rules match. Voice conversation is on by default (requires HTTPS; HTTPS is strongly recommended for text chat as well) — turn it off in Voice settings if you don't want it.

While the configuration is incomplete, the widget is not loaded and no external communication is triggered.

== Frequently Asked Questions ==

= Do I need an account with you, or your server? =
No. NeccoSpeak is self-contained: it needs only WordPress and your own OpenAI API key.

= Is it really free? =
The plugin: yes — free and open source (GPL). The AI usage: billed by OpenAI to your own account (your own key), at OpenAI's rates. Check OpenAI's official pricing for details.

= Does the chatbot know my site content? =
By default it answers from the AI model plus the system prompt you write — put your key business facts (hours, prices, policies) in the system prompt and it will use them. If you turn on the optional site content search and page directory (both off by default, on the AI settings tab's Content sync card), it can also quote excerpts from your own published posts and pages and point visitors to the right one.

= Is the API key handled securely? =
The key is stored encrypted in the database with AES-256-CBC. It is never output to the front end (browser) and is masked in the admin screen.

= Can visitors run up my OpenAI bill? =
The chat endpoint has per-IP, per-minute rate limiting plus site-wide hourly and daily caps; voice token issuance has per-IP and site-wide daily caps. When a cap is exceeded, responses are temporarily limited (503). Beyond that, display rules let you reduce exposure to abuse by limiting which pages show the widget. The chat API also refuses requests that do not come from a page on your own site (a same-origin check), but since origins can be forged, the rate limits remain the real line of defense.

= What do I need for voice chat? =
Voice chat is on by default (the "Enable voice conversation" toggle in Voice settings) — just serve your site over **HTTPS (SSL)**. Browser microphone permission is requested when it is used. Audio goes from the browser directly to OpenAI over WebRTC using a short-lived token — your API key is never handed to the browser.

= Will it slow down my site? =
While the plugin is unconfigured (disabled or no key), nothing is loaded on the front end at all. Once active, it loads a small widget only on the pages your display rules match.

= Do I need to update the plugin when a new model is released? =
No. The model name is free-form, and "Fetch available models" retrieves the list of models currently available to your API key so you can pick one (metadata only — no token billing).

= Are responses displayed as a stream? =
Yes. Replies appear progressively, typewriter-style, instead of all at once. The reply your browser has already received is revealed a bit at a time, so nothing extra is needed from your server. The effect also respects the operating system's "reduce motion" setting.

= Does the content search send my pages to OpenAI? =
No — the search itself runs entirely on your own server, against your own database. Only the excerpts actually selected as relevant to a visitor's question are sent to OpenAI, the same way any other part of the prompt is.

= Are the optional tools able to change anything on my site? =
No. Both tools (site search, page lookup) are read-only — the assistant can look things up but can never create, edit, or delete anything. The current date and time is supplied automatically and is not a tool at all, so it cannot change anything either.

= Why does voice not connect on my network? =
Voice connects the browser directly to OpenAI (WebRTC). Because OpenAI's Realtime API does not require STUN/TURN, this plugin does not use a STUN/TURN server by default. On a few highly restrictive networks (UDP blocked, symmetric NAT, etc.), voice may fail to connect; please use text chat there. Advanced users can set their own STUN/TURN servers via window.NECCOSPEAK_RTC_ICE_SERVERS (in that case, that destination becomes something you must disclose).

= Why can I not turn the voice volume all the way down on Android? =
This is a Chrome-for-Android behavior, not something this plugin controls: while a voice call is active, the call's audio is tied to the device's in-call volume control, and Android will not let that be lowered all the way to silent. To fully silence the assistant's voice during a call, use the widget's own speaker-mute button in the voice control bar instead of the device volume buttons — that mutes the assistant completely, on any device.

= I updated the plugin but the widget still looks/behaves like the old version =
After updating, purge any caching plugin (page cache and, if it has one, a separate "minify/combine JS" cache) and any CDN sitting in front of your site, then hard-reload the page. The widget's front-end JavaScript is loaded from several nested files that browsers and CDNs are free to cache independently of the main plugin file's version number, so a stale copy of just one of those files can outlive the update. If the browser's developer console shows a "Version mismatch" warning, that confirms this is exactly what happened.

== Screenshots ==

1. The AI chatbot widget with its home view and voice chat button — visitors ask by text, or talk to your site and hear it answer.
2. The Dashboard tab — the redesigned admin screen organized into nine tabs.
3. The AI settings tab — save your OpenAI API key and define the assistant's role and goals.
4. The Voice settings tab — pick the realtime model and voice, and tune how conversations flow.
5. The Preview tab — check the widget live inside the admin screen before publishing changes.

== External Services ==

This plugin communicates with the external service **OpenAI API** (api.openai.com) to generate AI responses. This is a core feature of the plugin, and communication occurs when the AI chat / voice features are used. Voice conversation is a direct connection (WebRTC) to the OpenAI Realtime API, and this plugin does not use a STUN/TURN server by default (no additional communication to third-party servers occurs).

When the optional "Link check" setting is on, the plugin also makes a request to **this site's own host** — never to any third party — solely to confirm that a link the assistant produced actually points to a real page before showing it to a visitor. Every outbound request (to OpenAI or to this site's own host) passes through a single allow-list that rejects private and link-local IP addresses after DNS resolution; a request to this site's own host is exempt from that IP check only, so sites behind Docker, a reverse proxy, or on an intranet keep working. The optional site content search, the optional tools, and the optional Contact Form 7 hand-off contact no external service at all — they run entirely on your own server and inside the visitor's own browser tab.

= OpenAI API =

* Destination endpoints:
    * https://api.openai.com/v1/chat/completions (generating text chat responses; via the PHP proxy)
    * https://api.openai.com/v1/realtime/client_secrets (issuing a short-lived token for voice conversation; PHP → OpenAI)
    * https://api.openai.com/v1/realtime/calls (establishing the WebRTC session for voice conversation; sent directly from the browser to OpenAI)
    * https://api.openai.com/v1/models (retrieving the list of available models; only when an administrator uses "Fetch available models" on the settings screen; PHP → OpenAI)
* Data sent (text chat): the message text entered by the visitor in the chat box, the conversation history, the configured system prompt, and the model name in use. These are sent to OpenAI via the PHP proxy at the moment the visitor sends a message.
* Data sent (model list retrieval): only when an administrator (manage_options capability) uses "Fetch available models" on the settings screen, a request is made from the server side (PHP) to OpenAI using the API key for authentication. Only the authentication credential is sent; no chat text or visitor data is sent. The response (a list of model IDs) is cached temporarily on the server side. This retrieves model metadata and incurs no OpenAI token billing. The API key is never handed to the browser.
* Data sent (when voice conversation is enabled): in addition to the short-lived token issuance request (PHP → OpenAI), the visitor's microphone audio is sent directly from the browser to OpenAI over WebRTC. Audio is sent from the moment the visitor starts a voice conversation.
* When data is sent: communication only occurs when a visitor sends a message or starts a voice conversation. When the plugin is not configured (disabled or no key set), no communication occurs at all.
* Purpose: to generate and return an AI response (text or voice) to the input.

Data sent to OpenAI is subject to OpenAI's terms and policies. Please be sure to review them before use.

* Terms of use: https://openai.com/policies/terms-of-use/
* Privacy policy: https://openai.com/policies/privacy-policy/
* API data usage policies: https://openai.com/policies/api-data-usage-policies/

= Additional CSS box (optional) =

The Additional CSS box on the Widget settings tab (requires the "unfiltered_html" capability to edit) intentionally allows `url(https://…)` and `@font-face { src: url(https://…) }` so administrators can reference their own hosted images/fonts. As a result, if an administrator adds such a rule, every front-end page view may cause the visitor's browser to fetch that resource from the third-party host referenced in the CSS. This is entirely administrator-controlled: the plugin ships with no Additional CSS by default and reaches no third-party host unless an admin explicitly adds one.

= Data stored on your site (no external service) =

When "Restore conversation" is enabled, the conversation is temporarily saved in the visitor's own browser (sessionStorage) so it survives a page navigation within the same tab; it is cleared when the tab is closed. When the "First-open modal" disclaimer is enabled, only a true/false "acknowledged" flag is saved (in localStorage or sessionStorage, depending on the setting). Neither of these is sent to any server, and neither stores any information that identifies the visitor.

**In your own WordPress database.** The plugin can also keep operational records in four of its own database tables on your server. Nothing here is ever transmitted anywhere — not to the developer, not to OpenAI, not to any third party — and every table is dropped when you uninstall the plugin.

* **Settings audit log** (on by default): which setting an administrator changed, when, and by whom. API keys and Additional CSS are recorded as redacted placeholders, never as their real values. Kept for 90 days by default.
* **Settings version history** (always on): up to the 30 most recent snapshots of your settings so you can compare and roll back — the API key is never included in a snapshot, and rollback is one click from the Change history tab.
* **Token usage totals** (on by default): per day, per model — request counts and token counts recorded from the API responses. It contains no visitor data of any kind.
* **Anonymous usage events** (**off by default**): counts of widget opens, messages, voice starts, and CTA clicks.
* **Site content index** (**off by default**): when the optional site content search is turned on, excerpts of your own published posts and pages are split and stored in the plugin's own database table so the assistant can quote them. Nothing here is ever sent anywhere except, when actually quoted in an answer, to OpenAI as part of that answer's prompt — the same way any other part of the prompt is sent.

**No IP addresses, no cookies, no visitor identifiers are ever stored** in any of these tables. Usage events are grouped by a random value that lives only in the visitor's own tab (sessionStorage) and disappears when that tab is closed — it cannot be used to recognise the same person again, on a later visit or in another tab.

**Per-page settings**: the optional per-page instructions, welcome-message override, widget show/hide choice, and content-search exclusion flag are stored as WordPress post meta on the page itself — editing them requires a publishing-level role, not just the ability to edit that page.

**Contact Form 7 hand-off** (on by default; nothing is loaded on sites where Contact Form 7 is not installed, or until you add a "Contact Form 7 (prefill)" CTA button): clicking that CTA stores the name/email/message prefill values entirely inside the visitor's own browser tab (sessionStorage) — never on your server. It expires automatically after five minutes and is deleted as soon as the form page reads it.

== Third-party resources ==

From 0.3.0, the widget frame document (the same-origin `?neccospeak_frame=1` page the front-end launcher opens inside an iframe) bundles the following third-party asset. It is served entirely from your own site — nothing here is fetched from a CDN or any other external host.

= Bootstrap Icons =

* Name: Bootstrap Icons
* Version: 1.13.1
* License: MIT
* Source: https://icons.getbootstrap.com/ (https://github.com/twbs/icons)
* Bundled files: `assets/widget-frame/vendor/bootstrap-icons/bootstrap-icons.css` (the complete upstream stylesheet; the only changes are the `@font-face` rule reduced to the bundled woff2 file and an added explanatory comment — no selector or property value is modified), `assets/widget-frame/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2` (the upstream v1.13.1 icon font, unmodified), `assets/widget-frame/vendor/bootstrap-icons/LICENSE` (the upstream MIT license text, unmodified).

== Upgrade Notice ==

= 0.3.0 =
Major rebuild of both the admin screen and the widget. The settings screen moved to its own top-level "NeccoSpeak" menu, and you must purge your page cache and CDN after updating. Your API key and settings carry over.

== Changelog ==

= 0.3.0 =
* **Admin screen**: rebuilt around a 9-tab layout (Dashboard, Widget settings, AI settings, Conversation flow, Voice settings, Model & advanced settings, Security, Preview, Change history) with a draft/publish workflow — edit freely, review a sticky summary of what changed, then publish when you are ready.
* Settings version history now keeps up to 30 generations with one-click rollback, backed by a full audit log of who changed what and when.
* The Preview tab renders the widget live inside a sandboxed iframe, so what you see while editing matches the real front end.
* Added an icon picker, a call-to-action (CTA) editor, and one-click fetching of the voice models available to your API key.
* **Chat widget**: rebuilt as a same-origin document (`?neccospeak_frame=1`) that the launcher opens inside an iframe, with its own dedicated Content-Security-Policy — script execution is restricted to same-origin plus a per-request nonce, and network access is restricted to this site and `api.openai.com` only.
* Home view with quick actions and CTA buttons, Markdown-formatted replies, session restore across page navigations, and full theme/launcher customization.
* Bootstrap Icons 1.13.1 is now bundled locally for the widget (see "Third-party resources" above) — no CDN request.
* **Voice chat**: the voice session now survives switching between UI views instead of reconnecting from scratch; visitors can switch microphone devices mid-call; voice now follows the visitor's own language; and voice conversation is now blocked while its browser tab is inactive.
* Voice fixes: a duplicate greeting on reconnect, a conversation-history ordering bug, the microphone button occasionally failing to reappear, and a crash when the status indicator is turned off.
* **Content and tools**: per-page instructions, an optional page directory, optional read-only tools (site search, page lookup), an optional site content index for on-site search, Contact Form 7 hand-off, and the current date and time now always supplied to both text chat and voice conversation.
* **Changes you should know about before updating:**
    1. The settings screen has moved from "Settings → NeccoSpeak" to its own top-level "NeccoSpeak" menu in the admin sidebar — update any bookmarks.
    2. **Purge your page cache and any CDN after updating.** Old cached HTML that still references the previous `assets/widget.js` / `assets/widget.css` front end will 404 until the cache is cleared.
    3. Server-side storage of the conversation log has been removed entirely, and its database table is dropped automatically on update. 0.1.1 never shipped this feature, so no existing data is lost.
    4. The visitor feedback rating, the first-visit privacy notice, and the "current date/time" tool setting have all been removed. The date and time are now supplied automatically at all times instead — a strict improvement, not a loss of capability.
    5. The previous widget front end has been replaced by the iframe + CSP approach described above.
    6. Voice conversation is now blocked while its browser tab is inactive (new behavior, see "Voice chat" above).
    7. Voice chat, quick actions/CTAs, the read-only tools, per-page instructions, and the Contact Form 7 hand-off are now on by default — turn any of them off in the relevant settings tab if you don't want them.
    8. **Your API key, display rules, appearance settings, and rate limits all carry over unchanged.**

= 0.1.1 =
* Internationalization: all user-facing strings, including the admin settings screen, now use English as the source language, so the plugin is fully translatable.
* Added a bundled Japanese (ja) translation — sites running in Japanese keep a fully localized admin screen and widget.
* Replaced emoji glyphs (launcher, close, microphone) with crisp inline SVG icons that follow the widget's brand color and button states.
* No functional changes to chat, voice, security, or data handling.

= 0.1.0 =
* Initial release.
* Self-contained, BYO-key lightweight AI chat widget.
* Text chat (relayed to OpenAI via a thin PHP proxy, full response returned).
* Optional voice conversation (off by default; short-lived token + WebRTC connecting the browser directly to OpenAI; HTTPS required).
* API key stored encrypted with AES-256-CBC, never sent to the front end, masked in the admin screen.
* Per-IP rate limiting, display rules (all pages / specific URLs only / exclude), appearance customization.
* Zero loading and zero external communication when not configured.
