ask-junkie 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,53 @@
1
+ /**
2
+ * OpenRouter Provider
3
+ * Access multiple AI models through one API
4
+ */
5
+
6
+ import { BaseProvider } from './BaseProvider.js';
7
+
8
+ export class OpenRouterProvider extends BaseProvider {
9
+ getDefaultModel() {
10
+ return 'meta-llama/llama-3-8b-instruct';
11
+ }
12
+
13
+ getEndpoint() {
14
+ return 'https://openrouter.ai/api/v1/chat/completions';
15
+ }
16
+
17
+ async sendMessage(message, context, history = []) {
18
+ const messages = [
19
+ { role: 'system', content: context },
20
+ ...this._formatHistory(history),
21
+ { role: 'user', content: message }
22
+ ];
23
+
24
+ const response = await this._fetch(this.getEndpoint(), {
25
+ method: 'POST',
26
+ headers: {
27
+ 'Content-Type': 'application/json',
28
+ 'Authorization': `Bearer ${this.apiKey}`,
29
+ 'HTTP-Referer': window.location.origin,
30
+ 'X-Title': document.title || 'Ask Junkie Chatbot'
31
+ },
32
+ body: JSON.stringify({
33
+ model: this.model,
34
+ messages: messages,
35
+ temperature: 0.7,
36
+ max_tokens: 1000
37
+ })
38
+ });
39
+
40
+ if (!response.ok) {
41
+ const error = await response.json().catch(() => ({}));
42
+ throw new Error(error.error?.message || `OpenRouter API error: ${response.status}`);
43
+ }
44
+
45
+ const data = await response.json();
46
+
47
+ if (data.choices?.[0]?.message?.content) {
48
+ return data.choices[0].message.content;
49
+ }
50
+
51
+ throw new Error('Invalid response from OpenRouter API');
52
+ }
53
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Firebase Analytics Logger
3
+ * Logs chat interactions to Firebase Firestore for centralized analytics
4
+ */
5
+
6
+ export class FirebaseLogger {
7
+ static FIREBASE_API_KEY = 'AIzaSyDPDWA7s6dauaPup9DFz0hMrQvDYPtEWwo';
8
+ static FIREBASE_PROJECT_ID = 'ai-chatbot-fe4a2';
9
+
10
+ constructor(config = {}) {
11
+ this.enabled = config.enabled !== false;
12
+ this.siteId = config.siteId || this._getSiteId();
13
+ this.userId = config.userId || null; // User ID from sdkKeys lookup
14
+ this.registered = false;
15
+ }
16
+
17
+ /**
18
+ * Get a sanitized site ID from hostname
19
+ */
20
+ _getSiteId() {
21
+ return window.location.hostname.replace(/[^a-zA-Z0-9.-]/g, '_');
22
+ }
23
+
24
+ /**
25
+ * Get Firestore REST API endpoint
26
+ */
27
+ _getEndpoint(path) {
28
+ return `https://firestore.googleapis.com/v1/projects/${FirebaseLogger.FIREBASE_PROJECT_ID}/databases/(default)/documents/${path}?key=${FirebaseLogger.FIREBASE_API_KEY}`;
29
+ }
30
+
31
+ /**
32
+ * Get the base path for this API key's data
33
+ * Uses new nested structure: users/{userId}/apiKeys/{keyId}
34
+ */
35
+ _getBasePath() {
36
+ if (this.userId && this.siteId) {
37
+ return `users/${this.userId}/apiKeys/${this.siteId}`;
38
+ }
39
+ // Fallback to old structure for backwards compatibility
40
+ return `sites/${this.siteId}`;
41
+ }
42
+
43
+ /**
44
+ * Register or update site analytics info
45
+ * For new user-based path, we SKIP this to avoid overwriting dashboard data
46
+ * Only used for legacy sites/ path
47
+ */
48
+ async registerSite() {
49
+ if (this.registered) return;
50
+
51
+ // Skip registration for new user-based path - dashboard already created the document
52
+ // Only the chats subcollection should be written to
53
+ if (this.userId && this.siteId) {
54
+ console.log('[AskJunkie] Skipping site registration for SDK-managed key');
55
+ this.registered = true;
56
+ return;
57
+ }
58
+
59
+ // Legacy path: sites/{siteId} - still needs registration
60
+ try {
61
+ const url = this._getEndpoint(this._getBasePath());
62
+
63
+ await fetch(url, {
64
+ method: 'PATCH',
65
+ headers: { 'Content-Type': 'application/json' },
66
+ body: JSON.stringify({
67
+ fields: {
68
+ domain: { stringValue: window.location.hostname },
69
+ site_name: { stringValue: document.title || 'Unknown' },
70
+ site_url: { stringValue: window.location.origin },
71
+ sdk_version: { stringValue: '1.1.6' },
72
+ platform: { stringValue: 'JavaScript SDK' },
73
+ last_active: { stringValue: new Date().toISOString() }
74
+ }
75
+ })
76
+ });
77
+
78
+ this.registered = true;
79
+ } catch (error) {
80
+ console.warn('[AskJunkie] Failed to register site:', error);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Log a chat interaction
86
+ */
87
+ async logChat(data) {
88
+ if (!this.enabled) return;
89
+
90
+ // Ensure site is registered first
91
+ await this.registerSite();
92
+
93
+ try {
94
+ const url = this._getEndpoint(`${this._getBasePath()}/chats`);
95
+
96
+ await fetch(url, {
97
+ method: 'POST',
98
+ headers: { 'Content-Type': 'application/json' },
99
+ body: JSON.stringify({
100
+ fields: {
101
+ session_id: { stringValue: data.sessionId || 'unknown' },
102
+ user_message: { stringValue: data.userMessage || '' },
103
+ ai_response: { stringValue: data.aiResponse || '' },
104
+ page_url: { stringValue: data.pageUrl || window.location.href },
105
+ timestamp: { stringValue: new Date().toISOString() },
106
+ user_agent: { stringValue: navigator.userAgent || '' }
107
+ }
108
+ })
109
+ });
110
+ } catch (error) {
111
+ console.warn('[AskJunkie] Failed to log chat:', error);
112
+ }
113
+ }
114
+ }
115
+