@biggora/claude-plugins 1.1.1 → 1.2.2
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.
- package/.claude/settings.local.json +3 -1
- package/README.md +24 -17
- package/package.json +1 -1
- package/registry/registry.json +319 -244
- package/specs/coding.md +24 -0
- package/specs/pod.md +2 -0
- package/src/skills/captcha/README.md +221 -0
- package/src/skills/captcha/SKILL.md +355 -0
- package/src/skills/captcha/references/captcha-types.md +254 -0
- package/src/skills/captcha/references/services.md +172 -0
- package/src/skills/captcha/references/stealth.md +238 -0
- package/src/skills/captcha/scripts/solve_captcha.py +323 -0
- package/src/skills/captcha/scripts/solve_image_grid.py +350 -0
- package/src/skills/codex-cli/SKILL.md +21 -11
- package/src/skills/gemini-cli/SKILL.md +27 -13
- package/src/skills/gemini-cli/references/commands.md +21 -14
- package/src/skills/gemini-cli/references/configuration.md +23 -18
- package/src/skills/gemini-cli/references/headless-and-scripting.md +7 -17
- package/src/skills/gemini-cli/references/mcp-and-extensions.md +12 -6
- package/src/skills/google-merchant-api/SKILL.md +581 -0
- package/src/skills/google-merchant-api/references/accounts.md +247 -0
- package/src/skills/google-merchant-api/references/content-api-legacy.md +216 -0
- package/src/skills/google-merchant-api/references/datasources.md +233 -0
- package/src/skills/google-merchant-api/references/inventories.md +201 -0
- package/src/skills/google-merchant-api/references/migration.md +267 -0
- package/src/skills/google-merchant-api/references/products.md +316 -0
- package/src/skills/google-merchant-api/references/promotions.md +201 -0
- package/src/skills/google-merchant-api/references/reports.md +240 -0
- package/src/skills/lv-aggregators-api/SKILL.md +113 -0
- package/src/skills/lv-aggregators-api/references/integration-guide.md +368 -0
- package/src/skills/lv-aggregators-api/references/kurpirkt.md +103 -0
- package/src/skills/lv-aggregators-api/references/salidzini.md +122 -0
- package/src/skills/notebook-lm/SKILL.md +1 -1
- package/src/skills/screen-recording/SKILL.md +243 -213
- package/src/skills/screen-recording/references/design-patterns.md +4 -2
- package/src/skills/screen-recording/references/ffmpeg-recording.md +473 -0
- package/src/skills/screen-recording/references/{approach1-programmatic.md → programmatic-generation.md} +45 -22
- package/src/skills/screen-recording/references/python-fallback.md +222 -0
- package/src/skills/tailwindcss-best-practices/SKILL.md +180 -0
- package/src/skills/tailwindcss-best-practices/references/best-practices-utility-patterns.md +87 -0
- package/src/skills/tailwindcss-best-practices/references/core-installation.md +109 -0
- package/src/skills/tailwindcss-best-practices/references/core-preflight.md +200 -0
- package/src/skills/tailwindcss-best-practices/references/core-responsive.md +163 -0
- package/src/skills/tailwindcss-best-practices/references/core-source-detection.md +114 -0
- package/src/skills/tailwindcss-best-practices/references/core-theme.md +108 -0
- package/src/skills/tailwindcss-best-practices/references/core-utility-classes.md +59 -0
- package/src/skills/tailwindcss-best-practices/references/core-variants.md +204 -0
- package/src/skills/tailwindcss-best-practices/references/effects-form-controls.md +76 -0
- package/src/skills/tailwindcss-best-practices/references/effects-mask.md +91 -0
- package/src/skills/tailwindcss-best-practices/references/effects-scroll-snap.md +59 -0
- package/src/skills/tailwindcss-best-practices/references/effects-text-shadow.md +78 -0
- package/src/skills/tailwindcss-best-practices/references/effects-transition-animation.md +80 -0
- package/src/skills/tailwindcss-best-practices/references/effects-visibility-interactivity.md +82 -0
- package/src/skills/tailwindcss-best-practices/references/features-content-detection.md +175 -0
- package/src/skills/tailwindcss-best-practices/references/features-custom-styles.md +203 -0
- package/src/skills/tailwindcss-best-practices/references/features-dark-mode.md +137 -0
- package/src/skills/tailwindcss-best-practices/references/features-functions-directives.md +241 -0
- package/src/skills/tailwindcss-best-practices/references/features-upgrade.md +160 -0
- package/src/skills/tailwindcss-best-practices/references/layout-aspect-ratio.md +39 -0
- package/src/skills/tailwindcss-best-practices/references/layout-columns.md +80 -0
- package/src/skills/tailwindcss-best-practices/references/layout-display.md +110 -0
- package/src/skills/tailwindcss-best-practices/references/layout-flexbox.md +112 -0
- package/src/skills/tailwindcss-best-practices/references/layout-grid.md +87 -0
- package/src/skills/tailwindcss-best-practices/references/layout-height.md +97 -0
- package/src/skills/tailwindcss-best-practices/references/layout-inset.md +103 -0
- package/src/skills/tailwindcss-best-practices/references/layout-logical-properties.md +92 -0
- package/src/skills/tailwindcss-best-practices/references/layout-margin.md +126 -0
- package/src/skills/tailwindcss-best-practices/references/layout-min-max-sizing.md +63 -0
- package/src/skills/tailwindcss-best-practices/references/layout-object-fit-position.md +64 -0
- package/src/skills/tailwindcss-best-practices/references/layout-overflow.md +57 -0
- package/src/skills/tailwindcss-best-practices/references/layout-padding.md +77 -0
- package/src/skills/tailwindcss-best-practices/references/layout-position.md +85 -0
- package/src/skills/tailwindcss-best-practices/references/layout-tables.md +67 -0
- package/src/skills/tailwindcss-best-practices/references/layout-width.md +102 -0
- package/src/skills/tailwindcss-best-practices/references/transform-base.md +68 -0
- package/src/skills/tailwindcss-best-practices/references/transform-rotate.md +70 -0
- package/src/skills/tailwindcss-best-practices/references/transform-scale.md +83 -0
- package/src/skills/tailwindcss-best-practices/references/transform-skew.md +62 -0
- package/src/skills/tailwindcss-best-practices/references/transform-translate.md +77 -0
- package/src/skills/tailwindcss-best-practices/references/typography-font-text.md +142 -0
- package/src/skills/tailwindcss-best-practices/references/typography-list-style.md +65 -0
- package/src/skills/tailwindcss-best-practices/references/typography-text-align.md +60 -0
- package/src/skills/tailwindcss-best-practices/references/visual-background.md +76 -0
- package/src/skills/tailwindcss-best-practices/references/visual-border.md +108 -0
- package/src/skills/tailwindcss-best-practices/references/visual-effects.md +111 -0
- package/src/skills/tailwindcss-best-practices/references/visual-svg.md +82 -0
- package/src/skills/test-mobile-app/SKILL.md +11 -6
- package/src/skills/test-mobile-app/scripts/analyze_apk.py +15 -4
- package/src/skills/test-mobile-app/scripts/check_environment.py +5 -5
- package/src/skills/test-mobile-app/scripts/run_tests.py +1 -1
- package/src/skills/test-web-ui/SKILL.md +264 -84
- package/src/skills/test-web-ui/scripts/discover.py +25 -12
- package/src/skills/test-web-ui/scripts/run_tests.py +3 -2
- package/src/skills/tm-search/SKILL.md +242 -106
- package/src/skills/tm-search/references/scraping-fallback.md +60 -95
- package/src/skills/tm-search/scripts/tm_search.py +453 -375
- package/src/skills/vite-best-practices/SKILL.md +115 -0
- package/src/skills/vite-best-practices/references/build-and-ssr.md +255 -0
- package/src/skills/vite-best-practices/references/core-config.md +231 -0
- package/src/skills/vite-best-practices/references/core-features.md +222 -0
- package/src/skills/vite-best-practices/references/core-plugin-api.md +294 -0
- package/src/skills/vite-best-practices/references/environment-api.md +108 -0
- package/src/skills/vite-best-practices/references/rolldown-migration.md +242 -0
- package/src/skills/screen-recording/references/approach2-xvfb.md +0 -232
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
# Integration Guide
|
|
2
|
+
|
|
3
|
+
Code examples and strategies for generating XML product feeds for Latvian aggregators.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Dual-Feed Strategy](#dual-feed-strategy)
|
|
8
|
+
2. [Node.js Implementation](#nodejs-implementation)
|
|
9
|
+
3. [PHP Implementation](#php-implementation)
|
|
10
|
+
4. [Python Implementation](#python-implementation)
|
|
11
|
+
5. [Validation](#validation)
|
|
12
|
+
6. [Scheduling](#scheduling)
|
|
13
|
+
7. [Common Pitfalls](#common-pitfalls)
|
|
14
|
+
|
|
15
|
+
## Dual-Feed Strategy
|
|
16
|
+
|
|
17
|
+
Since Salidzini.lv and KurPirkt.lv share a very similar XML structure, use a single product data source and generate both feeds with platform-specific adjustments:
|
|
18
|
+
|
|
19
|
+
1. Query products from your database
|
|
20
|
+
2. Map fields to each platform's tag names
|
|
21
|
+
3. Generate separate XML files and host them at distinct URLs
|
|
22
|
+
|
|
23
|
+
Key field mappings between platforms:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
brand -> <brand> (Salidzini) | <manufacturer> (KurPirkt)
|
|
27
|
+
delivery -> <delivery_latvija> | <delivery_cost_riga>
|
|
28
|
+
category -> "A >> B >> C" | "A > B > C"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Node.js Implementation
|
|
32
|
+
|
|
33
|
+
### Basic Feed Generator
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
import { createWriteStream } from 'fs';
|
|
37
|
+
import { pipeline } from 'stream/promises';
|
|
38
|
+
|
|
39
|
+
// Platform-specific field mappings
|
|
40
|
+
const PLATFORM_CONFIG = {
|
|
41
|
+
salidzini: {
|
|
42
|
+
brandTag: 'brand',
|
|
43
|
+
deliveryTag: 'delivery_latvija',
|
|
44
|
+
categorySeparator: ' >> ',
|
|
45
|
+
extraFields: (product) => `
|
|
46
|
+
<model>${escapeXml(product.model || '')}</model>
|
|
47
|
+
<color>${escapeXml(product.color || '')}</color>
|
|
48
|
+
<mpn>${escapeXml(product.mpn || '')}</mpn>
|
|
49
|
+
<ean>${escapeXml(product.ean || '')}</ean>
|
|
50
|
+
<delivery_days_latvija>${product.deliveryDays || ''}</delivery_days_latvija>
|
|
51
|
+
<delivery_days_shop>${product.pickupDays || ''}</delivery_days_shop>
|
|
52
|
+
<service_fee>${product.serviceFee || ''}</service_fee>
|
|
53
|
+
<adult>${product.isAdult ? 'yes' : 'no'}</adult>`,
|
|
54
|
+
},
|
|
55
|
+
kurpirkt: {
|
|
56
|
+
brandTag: 'manufacturer',
|
|
57
|
+
deliveryTag: 'delivery_cost_riga',
|
|
58
|
+
categorySeparator: ' > ',
|
|
59
|
+
extraFields: () => '',
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
function escapeXml(str) {
|
|
64
|
+
return String(str)
|
|
65
|
+
.replace(/&/g, '&')
|
|
66
|
+
.replace(/</g, '<')
|
|
67
|
+
.replace(/>/g, '>')
|
|
68
|
+
.replace(/"/g, '"')
|
|
69
|
+
.replace(/'/g, ''');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function generateItem(product, platform) {
|
|
73
|
+
const config = PLATFORM_CONFIG[platform];
|
|
74
|
+
const categoryPath = product.categories
|
|
75
|
+
?.join(config.categorySeparator) || '';
|
|
76
|
+
|
|
77
|
+
return ` <item>
|
|
78
|
+
<name>${escapeXml(product.name)}</name>
|
|
79
|
+
<link>${escapeXml(product.url)}</link>
|
|
80
|
+
<price>${product.price.toFixed(2)}</price>
|
|
81
|
+
<image>${escapeXml(product.imageUrl || '')}</image>
|
|
82
|
+
<${config.brandTag}>${escapeXml(product.brand || '')}</${config.brandTag}>
|
|
83
|
+
<category_full>${categoryPath}</category_full>
|
|
84
|
+
<category_link>${escapeXml(product.categoryUrl || '')}</category_link>
|
|
85
|
+
<in_stock>${product.stock ?? ''}</in_stock>
|
|
86
|
+
<${config.deliveryTag}>${product.deliveryCost ?? ''}</${config.deliveryTag}>
|
|
87
|
+
<used>${product.isUsed ? '1' : ''}</used>${config.extraFields(product)}
|
|
88
|
+
</item>`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function generateFeed(products, platform, outputPath) {
|
|
92
|
+
const stream = createWriteStream(outputPath);
|
|
93
|
+
stream.write('<?xml version="1.0" encoding="utf-8" ?>\n<root>\n');
|
|
94
|
+
|
|
95
|
+
for (const product of products) {
|
|
96
|
+
stream.write(generateItem(product, platform) + '\n');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
stream.write('</root>\n');
|
|
100
|
+
stream.end();
|
|
101
|
+
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
stream.on('finish', resolve);
|
|
104
|
+
stream.on('error', reject);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Usage
|
|
109
|
+
const products = await fetchProductsFromDatabase();
|
|
110
|
+
await Promise.all([
|
|
111
|
+
generateFeed(products, 'salidzini', './public/export/salidzini.xml'),
|
|
112
|
+
generateFeed(products, 'kurpirkt', './public/export/kurpirkt.xml'),
|
|
113
|
+
]);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Express.js Dynamic Feed Endpoint
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
import express from 'express';
|
|
120
|
+
|
|
121
|
+
const app = express();
|
|
122
|
+
|
|
123
|
+
app.get('/export/:platform.xml', async (req, res) => {
|
|
124
|
+
const { platform } = req.params;
|
|
125
|
+
if (!['salidzini', 'kurpirkt'].includes(platform)) {
|
|
126
|
+
return res.status(404).send('Unknown platform');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const products = await fetchProductsFromDatabase();
|
|
130
|
+
|
|
131
|
+
res.set('Content-Type', 'application/xml; charset=utf-8');
|
|
132
|
+
res.write('<?xml version="1.0" encoding="utf-8" ?>\n<root>\n');
|
|
133
|
+
|
|
134
|
+
for (const product of products) {
|
|
135
|
+
res.write(generateItem(product, platform) + '\n');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
res.end('</root>\n');
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## PHP Implementation
|
|
143
|
+
|
|
144
|
+
```php
|
|
145
|
+
<?php
|
|
146
|
+
header('Content-Type: application/xml; charset=utf-8');
|
|
147
|
+
|
|
148
|
+
$platform = $_GET['platform'] ?? 'salidzini';
|
|
149
|
+
|
|
150
|
+
// Fetch products from your database
|
|
151
|
+
$products = getProductsFromDB();
|
|
152
|
+
|
|
153
|
+
$xml = new XMLWriter();
|
|
154
|
+
$xml->openMemory();
|
|
155
|
+
$xml->startDocument('1.0', 'UTF-8');
|
|
156
|
+
$xml->startElement('root');
|
|
157
|
+
|
|
158
|
+
foreach ($products as $product) {
|
|
159
|
+
$xml->startElement('item');
|
|
160
|
+
|
|
161
|
+
$xml->writeElement('name', $product['name']);
|
|
162
|
+
$xml->writeElement('link', $product['url']);
|
|
163
|
+
$xml->writeElement('price', number_format($product['price'], 2, '.', ''));
|
|
164
|
+
$xml->writeElement('image', $product['image_url'] ?? '');
|
|
165
|
+
|
|
166
|
+
// Platform-specific brand tag
|
|
167
|
+
$brandTag = $platform === 'kurpirkt' ? 'manufacturer' : 'brand';
|
|
168
|
+
$xml->writeElement($brandTag, $product['brand'] ?? '');
|
|
169
|
+
|
|
170
|
+
// Category with platform-specific separator
|
|
171
|
+
$separator = $platform === 'kurpirkt' ? ' > ' : ' >> ';
|
|
172
|
+
$categoryPath = implode($separator, $product['categories'] ?? []);
|
|
173
|
+
$xml->writeElement('category_full', $categoryPath);
|
|
174
|
+
$xml->writeElement('category_link', $product['category_url'] ?? '');
|
|
175
|
+
|
|
176
|
+
$xml->writeElement('in_stock', $product['stock'] ?? '');
|
|
177
|
+
|
|
178
|
+
// Platform-specific delivery tag
|
|
179
|
+
$deliveryTag = $platform === 'kurpirkt' ? 'delivery_cost_riga' : 'delivery_latvija';
|
|
180
|
+
$xml->writeElement($deliveryTag, $product['delivery_cost'] ?? '');
|
|
181
|
+
|
|
182
|
+
$xml->writeElement('used', $product['is_used'] ? '1' : '');
|
|
183
|
+
|
|
184
|
+
// Salidzini-specific extra fields
|
|
185
|
+
if ($platform === 'salidzini') {
|
|
186
|
+
$xml->writeElement('model', $product['model'] ?? '');
|
|
187
|
+
$xml->writeElement('color', $product['color'] ?? '');
|
|
188
|
+
$xml->writeElement('mpn', $product['mpn'] ?? '');
|
|
189
|
+
$xml->writeElement('ean', $product['ean'] ?? '');
|
|
190
|
+
$xml->writeElement('delivery_days_latvija', $product['delivery_days'] ?? '');
|
|
191
|
+
$xml->writeElement('delivery_days_shop', $product['pickup_days'] ?? '');
|
|
192
|
+
$xml->writeElement('adult', $product['is_adult'] ? 'yes' : 'no');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
$xml->endElement(); // item
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
$xml->endElement(); // root
|
|
199
|
+
echo $xml->outputMemory();
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Python Implementation
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
import xml.etree.ElementTree as ET
|
|
206
|
+
from xml.dom import minidom
|
|
207
|
+
|
|
208
|
+
PLATFORM_CONFIG = {
|
|
209
|
+
'salidzini': {
|
|
210
|
+
'brand_tag': 'brand',
|
|
211
|
+
'delivery_tag': 'delivery_latvija',
|
|
212
|
+
'category_sep': ' >> ',
|
|
213
|
+
},
|
|
214
|
+
'kurpirkt': {
|
|
215
|
+
'brand_tag': 'manufacturer',
|
|
216
|
+
'delivery_tag': 'delivery_cost_riga',
|
|
217
|
+
'category_sep': ' > ',
|
|
218
|
+
},
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
def generate_feed(products: list[dict], platform: str, output_path: str):
|
|
222
|
+
config = PLATFORM_CONFIG[platform]
|
|
223
|
+
root = ET.Element('root')
|
|
224
|
+
|
|
225
|
+
for product in products:
|
|
226
|
+
item = ET.SubElement(root, 'item')
|
|
227
|
+
|
|
228
|
+
ET.SubElement(item, 'name').text = product['name']
|
|
229
|
+
ET.SubElement(item, 'link').text = product['url']
|
|
230
|
+
ET.SubElement(item, 'price').text = f"{product['price']:.2f}"
|
|
231
|
+
ET.SubElement(item, 'image').text = product.get('image_url', '')
|
|
232
|
+
|
|
233
|
+
ET.SubElement(item, config['brand_tag']).text = product.get('brand', '')
|
|
234
|
+
|
|
235
|
+
categories = product.get('categories', [])
|
|
236
|
+
ET.SubElement(item, 'category_full').text = config['category_sep'].join(categories)
|
|
237
|
+
ET.SubElement(item, 'category_link').text = product.get('category_url', '')
|
|
238
|
+
|
|
239
|
+
ET.SubElement(item, 'in_stock').text = str(product.get('stock', ''))
|
|
240
|
+
ET.SubElement(item, config['delivery_tag']).text = str(product.get('delivery_cost', ''))
|
|
241
|
+
ET.SubElement(item, 'used').text = '1' if product.get('is_used') else ''
|
|
242
|
+
|
|
243
|
+
# Salidzini-specific fields
|
|
244
|
+
if platform == 'salidzini':
|
|
245
|
+
ET.SubElement(item, 'model').text = product.get('model', '')
|
|
246
|
+
ET.SubElement(item, 'color').text = product.get('color', '')
|
|
247
|
+
ET.SubElement(item, 'mpn').text = product.get('mpn', '')
|
|
248
|
+
ET.SubElement(item, 'ean').text = product.get('ean', '')
|
|
249
|
+
ET.SubElement(item, 'delivery_days_latvija').text = str(product.get('delivery_days', ''))
|
|
250
|
+
ET.SubElement(item, 'delivery_days_shop').text = str(product.get('pickup_days', ''))
|
|
251
|
+
ET.SubElement(item, 'adult').text = 'yes' if product.get('is_adult') else 'no'
|
|
252
|
+
|
|
253
|
+
xml_str = minidom.parseString(ET.tostring(root, encoding='unicode')).toprettyxml(
|
|
254
|
+
indent=' ', encoding=None
|
|
255
|
+
)
|
|
256
|
+
# Replace default XML declaration with utf-8
|
|
257
|
+
xml_str = xml_str.replace(
|
|
258
|
+
"<?xml version=\"1.0\" ?>",
|
|
259
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
263
|
+
f.write(xml_str)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# Usage
|
|
267
|
+
products = fetch_products_from_db()
|
|
268
|
+
generate_feed(products, 'salidzini', 'export/salidzini.xml')
|
|
269
|
+
generate_feed(products, 'kurpirkt', 'export/kurpirkt.xml')
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Validation
|
|
273
|
+
|
|
274
|
+
### Salidzini.lv Validator
|
|
275
|
+
|
|
276
|
+
Use the built-in validator at `https://www.salidzini.lv/shops_info.php` — paste your feed URL at the bottom of the page.
|
|
277
|
+
|
|
278
|
+
### Manual Validation Checklist
|
|
279
|
+
|
|
280
|
+
Before submitting your feed, verify:
|
|
281
|
+
|
|
282
|
+
- [ ] XML is well-formed (valid UTF-8, proper encoding declaration)
|
|
283
|
+
- [ ] All required fields (`name`, `link`, `price`) are present for every item
|
|
284
|
+
- [ ] Prices are numeric with dot decimal separator (e.g., `305.77`)
|
|
285
|
+
- [ ] All URLs are absolute and accessible
|
|
286
|
+
- [ ] No duplicate products
|
|
287
|
+
- [ ] Product names are concise without promotional text
|
|
288
|
+
- [ ] Images have white backgrounds, no watermarks/logos
|
|
289
|
+
- [ ] Used/refurbished items have `<used>1</used>`
|
|
290
|
+
- [ ] No alcohol/tobacco products (Salidzini)
|
|
291
|
+
- [ ] Feed loads within 5 minutes (Salidzini)
|
|
292
|
+
- [ ] Stock quantities match what's shown on your shop pages
|
|
293
|
+
|
|
294
|
+
### Simple XML Validation Script (Node.js)
|
|
295
|
+
|
|
296
|
+
```javascript
|
|
297
|
+
import { readFileSync } from 'fs';
|
|
298
|
+
import { XMLParser } from 'fast-xml-parser';
|
|
299
|
+
|
|
300
|
+
const parser = new XMLParser();
|
|
301
|
+
const xml = readFileSync('export/salidzini.xml', 'utf-8');
|
|
302
|
+
const data = parser.parse(xml);
|
|
303
|
+
|
|
304
|
+
const items = Array.isArray(data.root.item) ? data.root.item : [data.root.item];
|
|
305
|
+
const errors = [];
|
|
306
|
+
|
|
307
|
+
items.forEach((item, i) => {
|
|
308
|
+
if (!item.name) errors.push(`Item ${i}: missing <name>`);
|
|
309
|
+
if (!item.link) errors.push(`Item ${i}: missing <link>`);
|
|
310
|
+
if (!item.price || isNaN(Number(item.price))) {
|
|
311
|
+
errors.push(`Item ${i}: invalid or missing <price>`);
|
|
312
|
+
}
|
|
313
|
+
if (item.name && String(item.name).length > 200) {
|
|
314
|
+
errors.push(`Item ${i}: name exceeds 200 characters`);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
console.log(`Checked ${items.length} items, found ${errors.length} errors`);
|
|
319
|
+
errors.forEach(e => console.error(e));
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Scheduling
|
|
323
|
+
|
|
324
|
+
### Cron (Linux)
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
# Regenerate feeds every day at 3 AM
|
|
328
|
+
0 3 * * * /usr/bin/node /var/www/shop/scripts/generate-feeds.js >> /var/log/feed-export.log 2>&1
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Node.js with node-cron
|
|
332
|
+
|
|
333
|
+
```javascript
|
|
334
|
+
import cron from 'node-cron';
|
|
335
|
+
|
|
336
|
+
// Run daily at 3 AM
|
|
337
|
+
cron.schedule('0 3 * * *', async () => {
|
|
338
|
+
console.log('Generating aggregator feeds...');
|
|
339
|
+
const products = await fetchProductsFromDatabase();
|
|
340
|
+
await generateFeed(products, 'salidzini', './public/export/salidzini.xml');
|
|
341
|
+
await generateFeed(products, 'kurpirkt', './public/export/kurpirkt.xml');
|
|
342
|
+
console.log('Feeds generated successfully');
|
|
343
|
+
});
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Windows Task Scheduler
|
|
347
|
+
|
|
348
|
+
```batch
|
|
349
|
+
schtasks /create /tn "GenerateAggregatorFeeds" /tr "node C:\shop\scripts\generate-feeds.js" /sc daily /st 03:00
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Common Pitfalls
|
|
353
|
+
|
|
354
|
+
1. **Wrong encoding** - Always use UTF-8. Latvian characters (ā, č, ē, ģ, ī, ķ, ļ, ņ, š, ū, ž) will break if the file isn't properly encoded.
|
|
355
|
+
|
|
356
|
+
2. **HTML entities in XML** - The category separator `>>` must be escaped as `>>` in Salidzini feeds. Use CDATA sections for fields that may contain special characters.
|
|
357
|
+
|
|
358
|
+
3. **Price format** - Use dot (`.`), never comma (`,`). `305.77` is correct, `305,77` will fail.
|
|
359
|
+
|
|
360
|
+
4. **Promotional text in names** - Aggregators actively check for and reject names with "Free shipping", "Best price", "Akcija", etc.
|
|
361
|
+
|
|
362
|
+
5. **Missing or low-quality images** - Images with logos, text overlays, or non-white backgrounds get rejected. Better to leave `<image>` empty than provide a non-compliant image.
|
|
363
|
+
|
|
364
|
+
6. **Stale feeds** - Update at least daily. If your feed shows products that are out of stock or have wrong prices, the aggregator may penalize your shop.
|
|
365
|
+
|
|
366
|
+
7. **Slow feed response** - Salidzini requires feed delivery within 5 minutes. For large catalogs (10,000+ items), pre-generate the XML file rather than building it dynamically per request.
|
|
367
|
+
|
|
368
|
+
8. **Duplicate products** - Same product with different colors/sizes should ideally be separate items with unique names, not duplicates of the same listing.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# KurPirkt.lv XML Feed Specification
|
|
2
|
+
|
|
3
|
+
Latvia's largest price comparison and product search portal (30,000-45,000 daily visitors, 1,200+ listed shops). Documentation source: `https://www.kurpirkt.lv/veikaliem.php`
|
|
4
|
+
|
|
5
|
+
## Registration
|
|
6
|
+
|
|
7
|
+
Submit an application via email to **kurpirkt@kurpirkt.lv** with:
|
|
8
|
+
|
|
9
|
+
1. URL to your product XML feed (e.g., `https://www.yourshop.lv/files/export/kurpirkt.php`)
|
|
10
|
+
2. Link to your shop's contact information page
|
|
11
|
+
3. Link to your shop's delivery information page
|
|
12
|
+
4. Your company's legal details (rekvizīti)
|
|
13
|
+
5. Email address for the shop's admin profile (e.g., `info@yourshop.lv`)
|
|
14
|
+
6. Confirmation that the KurPirkt.lv partner banner is placed on your shop
|
|
15
|
+
|
|
16
|
+
**Phone:** +371 26533032
|
|
17
|
+
|
|
18
|
+
Each application is reviewed and verified before being added to the KurPirkt.lv database. Confirmation email is sent upon approval. If no confirmation within one week, contact them again.
|
|
19
|
+
|
|
20
|
+
## Shop Requirements
|
|
21
|
+
|
|
22
|
+
- Products must have clearly displayed prices and payment methods
|
|
23
|
+
- All products must be deliverable or available for pickup in Latvia
|
|
24
|
+
- Delivery prices and terms must be clearly stated
|
|
25
|
+
- Clear product return process and warranty terms required
|
|
26
|
+
- KurPirkt.lv partner banner must be placed on shop's homepage
|
|
27
|
+
- Self-reviews on KurPirkt.lv are prohibited
|
|
28
|
+
- Misusing KurPirkt.lv database (copying, scraping) is prohibited
|
|
29
|
+
- Shops violating terms or consumer rights may be disconnected (with prior notice)
|
|
30
|
+
- KurPirkt.lv may charge for clicks from the search engine
|
|
31
|
+
- Terms changes are communicated at least 15 days before taking effect
|
|
32
|
+
- Shops can opt out of terms changes within 5 days before they take effect
|
|
33
|
+
- Shops can terminate cooperation with at least 5 days notice
|
|
34
|
+
- KurPirkt.lv can terminate cooperation with at least 30 days notice with stated reasons
|
|
35
|
+
|
|
36
|
+
## XML Feed Structure
|
|
37
|
+
|
|
38
|
+
```xml
|
|
39
|
+
<?xml version='1.0' encoding='utf-8' ?>
|
|
40
|
+
<root>
|
|
41
|
+
<item>
|
|
42
|
+
<name>Apple iPhone 13 PRO 512GB black</name>
|
|
43
|
+
<link>https://www.example.lv/page.php?prod=1234</link>
|
|
44
|
+
<price>1200.59</price>
|
|
45
|
+
<image>https://www.example.lv/bildes/apple_iphone_13_pro.jpg</image>
|
|
46
|
+
<manufacturer>Apple</manufacturer>
|
|
47
|
+
<category>Mobilie telefoni</category>
|
|
48
|
+
<category_full>Sakaru līdzekļi > Mobilie telefoni</category_full>
|
|
49
|
+
<category_link>https://www.example.lv/page.php?cat=12</category_link>
|
|
50
|
+
<in_stock>5</in_stock>
|
|
51
|
+
<delivery_cost_riga>2.25</delivery_cost_riga>
|
|
52
|
+
<used>0</used>
|
|
53
|
+
</item>
|
|
54
|
+
</root>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Field Reference
|
|
58
|
+
|
|
59
|
+
| Tag | Required | Description | Constraints |
|
|
60
|
+
|-----|----------|-------------|-------------|
|
|
61
|
+
| `<name>` | Yes | Product name | Brand + model + key identifiers |
|
|
62
|
+
| `<link>` | Yes | Full product page URL | Complete URL to the product |
|
|
63
|
+
| `<price>` | Yes | Price in EUR including VAT | Numeric, dot decimal separator |
|
|
64
|
+
| `<image>` | No | Product image URL | White background, no watermarks |
|
|
65
|
+
| `<manufacturer>` | No | Product manufacturer/brand | **Note:** Salidzini uses `<brand>` |
|
|
66
|
+
| `<category>` | No | Short category name | Single-level category |
|
|
67
|
+
| `<category_full>` | No | Full category path with hierarchy | Use `>` as separator (not `>>`) |
|
|
68
|
+
| `<category_link>` | No | Category page URL | |
|
|
69
|
+
| `<in_stock>` | No | Available stock quantity | Numeric value |
|
|
70
|
+
| `<delivery_cost_riga>` | No | Delivery cost to Riga | Numeric, EUR. **Note:** Salidzini uses `<delivery_latvija>` (all Latvia) |
|
|
71
|
+
| `<used>` | No | Used/demo product marker | `1` for used items, `0` for new. **Mandatory** value `1` for used/demo products. |
|
|
72
|
+
|
|
73
|
+
## Key Differences from Salidzini.lv
|
|
74
|
+
|
|
75
|
+
| Aspect | KurPirkt.lv | Salidzini.lv |
|
|
76
|
+
|--------|-------------|-------------|
|
|
77
|
+
| Brand tag | `<manufacturer>` | `<brand>` |
|
|
78
|
+
| Delivery cost tag | `<delivery_cost_riga>` (Riga only) | `<delivery_latvija>` (all Latvia) |
|
|
79
|
+
| Category separator | `>` | `>>` |
|
|
80
|
+
| Additional fields | Fewer (no model, color, mpn, ean, adult, medicine) | More detailed product attributes |
|
|
81
|
+
| Registration | Email application | Web form |
|
|
82
|
+
| Feed validator | Not mentioned | Available on shops_info page |
|
|
83
|
+
| Short category | `<category>` (single-level) | Not available |
|
|
84
|
+
|
|
85
|
+
## Banner Code
|
|
86
|
+
|
|
87
|
+
Place the KurPirkt.lv banner on your shop's homepage. Two size options:
|
|
88
|
+
|
|
89
|
+
**88x31px:**
|
|
90
|
+
```html
|
|
91
|
+
<a href="https://www.kurpirkt.lv" title="Kurpirkt.lv - visi Latvijas interneta veikali un cenas">
|
|
92
|
+
<img style="border:none;" alt="Kurpirkt.lv - visi Latvijas interneta veikali un cenas"
|
|
93
|
+
src="//www.kurpirkt.lv/media/kurpirkt88.gif" width="88" height="31">
|
|
94
|
+
</a>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**120x40px:**
|
|
98
|
+
```html
|
|
99
|
+
<a href="https://www.kurpirkt.lv" title="Kurpirkt.lv - visi Latvijas interneta veikali un cenas">
|
|
100
|
+
<img style="border:none;" alt="Kurpirkt.lv - visi Latvijas interneta veikali un cenas"
|
|
101
|
+
src="//www.kurpirkt.lv/media/kurpirkt120.gif" width="120" height="40">
|
|
102
|
+
</a>
|
|
103
|
+
```
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Salidzini.lv XML Feed Specification
|
|
2
|
+
|
|
3
|
+
Latvia's largest price comparison engine (~47,000 daily visitors, ~1.3 million monthly). Documentation source: `https://www.salidzini.lv/shops_info.php`
|
|
4
|
+
|
|
5
|
+
## Registration
|
|
6
|
+
|
|
7
|
+
1. Fill out the application form at `https://www.salidzini.lv/shops_info.php`
|
|
8
|
+
2. Provide your shop URL and contact email
|
|
9
|
+
3. Indicate whether you need XML feed creation assistance (available from 2 EUR/month)
|
|
10
|
+
4. After registration, shop name and domain cannot be changed
|
|
11
|
+
|
|
12
|
+
## Shop Requirements
|
|
13
|
+
|
|
14
|
+
- Products must be purchasable online with delivery available in Latvia
|
|
15
|
+
- Payment and delivery process must be clearly described
|
|
16
|
+
- Salidzini.lv partner banner must be placed on the shop's website
|
|
17
|
+
- XML product feed must be created and hosted (specification below)
|
|
18
|
+
- Shops may be disconnected upon receiving many complaints or suspicion of unfair practices
|
|
19
|
+
- Registering multiple shops with similar product ranges is prohibited
|
|
20
|
+
- Listing the same product multiple times is prohibited
|
|
21
|
+
- Public contests encouraging review placement are prohibited
|
|
22
|
+
- Self-reviews or paid reviews are prohibited
|
|
23
|
+
|
|
24
|
+
## XML Feed Structure
|
|
25
|
+
|
|
26
|
+
```xml
|
|
27
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
|
28
|
+
<root>
|
|
29
|
+
<item>
|
|
30
|
+
<!-- Required fields -->
|
|
31
|
+
<name>Samsung Galaxy S8 G950F Black</name>
|
|
32
|
+
<link>http://www.shop-name.lv/info/SamsungGalaxyS8G950FBlack/</link>
|
|
33
|
+
<price>305.77</price>
|
|
34
|
+
|
|
35
|
+
<!-- Optional fields -->
|
|
36
|
+
<image>http://www.shop-name.lv/images/SamsungGalaxyS8G950FBlack.jpg</image>
|
|
37
|
+
<category_full>Mobilie telefoni >> Samsung</category_full>
|
|
38
|
+
<category_link>http://www.shop-name.lv/Samsung</category_link>
|
|
39
|
+
<brand>Samsung</brand>
|
|
40
|
+
<model><![CDATA[Galaxy S8]]></model>
|
|
41
|
+
<color>Black</color>
|
|
42
|
+
<mpn>SM-G950F</mpn>
|
|
43
|
+
<ean>0644391356614</ean>
|
|
44
|
+
<in_stock>7</in_stock>
|
|
45
|
+
<delivery_latvija>1.90</delivery_latvija>
|
|
46
|
+
<delivery_days_latvija>5</delivery_days_latvija>
|
|
47
|
+
<delivery_days_shop>2</delivery_days_shop>
|
|
48
|
+
<service_fee>0.99</service_fee>
|
|
49
|
+
<used></used>
|
|
50
|
+
<adult>no</adult>
|
|
51
|
+
<over_the_counter_medicine></over_the_counter_medicine>
|
|
52
|
+
</item>
|
|
53
|
+
</root>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Field Reference
|
|
57
|
+
|
|
58
|
+
### Required Fields
|
|
59
|
+
|
|
60
|
+
| Tag | Description | Constraints |
|
|
61
|
+
|-----|-------------|-------------|
|
|
62
|
+
| `<name>` | Product name. Preferably only manufacturer, model, and code. | Max 200 characters. Must be concise, descriptive, no promo text. |
|
|
63
|
+
| `<link>` | Full product page URL on the registered domain. | Max 500 characters. Must be a complete URL. |
|
|
64
|
+
| `<price>` | Final price in EUR including VAT. | Numeric value with dot as decimal separator. Must include all additional costs except delivery. |
|
|
65
|
+
|
|
66
|
+
### Optional Fields
|
|
67
|
+
|
|
68
|
+
| Tag | Description | Constraints |
|
|
69
|
+
|-----|-------------|-------------|
|
|
70
|
+
| `<image>` | Product image URL. | Max 500 chars, max 16MB file size. Min 500x500px recommended. White background, no frames, no logos/watermarks/overlays. Leave empty if no proper image. |
|
|
71
|
+
| `<category_full>` | Full category path with parent categories. | Max 200 chars. Use `>>` as hierarchy separator. Must describe the product category, no promo words. |
|
|
72
|
+
| `<category_link>` | URL to the category page. | Max 500 characters. |
|
|
73
|
+
| `<brand>` | Product brand / manufacturer. | |
|
|
74
|
+
| `<model>` | Product model name. | CDATA supported. |
|
|
75
|
+
| `<color>` | Product color in English or Latvian. | |
|
|
76
|
+
| `<mpn>` | Manufacturer Part Number. | |
|
|
77
|
+
| `<ean>` | EAN code (also GTIN, UPC, JAN, ISBN). | |
|
|
78
|
+
| `<in_stock>` | Available quantity in warehouse for delivery to Riga within 4 business days. | Numeric value. Must match the quantity shown on the shop's product page. |
|
|
79
|
+
| `<delivery_latvija>` | Lowest delivery cost in Latvia (to address, parcel locker, or post office). | Numeric value. Leave empty if unavailable. |
|
|
80
|
+
| `<delivery_days_latvija>` | Minimum guaranteed delivery days in Latvia. Example: if delivery is 3-5 days, specify `5`. | Numeric value. Leave empty if unavailable. |
|
|
81
|
+
| `<delivery_days_shop>` | Maximum business days for free in-store pickup. Use `0` if available today. | Numeric value. Leave empty if unavailable. |
|
|
82
|
+
| `<service_fee>` | Purchase service/handling fee, if any. | Numeric value. |
|
|
83
|
+
| `<used>` | Mark used, refurbished, demo, or damaged products. | Value `1` is **mandatory** for such products. |
|
|
84
|
+
| `<adult>` | Mark sexual, nude, erotic, or intimate products. | Values: `yes`/`no`. **Mandatory** for adult product sellers. |
|
|
85
|
+
| `<over_the_counter_medicine>` | Mark over-the-counter medicines (no prescription needed). | Value `1`. For pharmaceutical sellers. |
|
|
86
|
+
|
|
87
|
+
## Naming Rules
|
|
88
|
+
|
|
89
|
+
Product names should follow the pattern: **Brand + Model + Key Identifier**
|
|
90
|
+
|
|
91
|
+
**Good:** `Samsung Galaxy S8 G950F Black`
|
|
92
|
+
**Bad:** `Super deal! Samsung Galaxy S8 - Free shipping - Best price - Buy now!`
|
|
93
|
+
|
|
94
|
+
Prohibited in names:
|
|
95
|
+
- "Ir uz vietas" (In stock), "Bezmaksas piegāde" (Free delivery), "Bezmaksas kredīts" (Free credit)
|
|
96
|
+
- "Labākā izvēle" (Best choice), "Augstākā kvalitāte" (Highest quality)
|
|
97
|
+
- "Iegādājies tagad" (Buy now), "Ideāli dāvanai" (Ideal for gift)
|
|
98
|
+
- "Super cena" (Super price), "Jaunums" (New), and similar promotional phrases
|
|
99
|
+
- Excessively long, unclear, or machine-translated names
|
|
100
|
+
|
|
101
|
+
## Image Rules
|
|
102
|
+
|
|
103
|
+
- Must represent the product's actual appearance
|
|
104
|
+
- White background, no frames
|
|
105
|
+
- No shop logo, price, or other unrelated imagery
|
|
106
|
+
- No overlaid text ("In stock", "Good product", "10 years on market", "Gift", "0% credit")
|
|
107
|
+
- If no suitable product image exists, use empty `<image></image>`
|
|
108
|
+
|
|
109
|
+
## Content Restrictions
|
|
110
|
+
|
|
111
|
+
- No alcohol or tobacco products
|
|
112
|
+
- Only products with delivery within 30 days
|
|
113
|
+
- Each product listed only once
|
|
114
|
+
- All information must match what's displayed on the shop's product page
|
|
115
|
+
|
|
116
|
+
## Feed Validation
|
|
117
|
+
|
|
118
|
+
Salidzini provides an XML feed validator at `https://www.salidzini.lv/shops_info.php` (bottom of page). Enter your feed URL to check for errors.
|
|
119
|
+
|
|
120
|
+
## Banner Code
|
|
121
|
+
|
|
122
|
+
The shop must display a Salidzini.lv banner. Three variants are available (blue, dark, light) at 190x60px. The banner images are hosted at `static.salidzini.lv/images/`.
|