bps-kit 1.0.1 → 1.0.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/package.json +1 -1
- package/templates/.agents/agents/backend-specialist.md +263 -0
- package/templates/.agents/agents/code-archaeologist.md +106 -0
- package/templates/.agents/agents/database-architect.md +226 -0
- package/templates/.agents/agents/debugger.md +225 -0
- package/templates/.agents/agents/devops-engineer.md +242 -0
- package/templates/.agents/agents/documentation-writer.md +104 -0
- package/templates/.agents/agents/explorer-agent.md +73 -0
- package/templates/.agents/agents/frontend-specialist.md +593 -0
- package/templates/.agents/agents/game-developer.md +162 -0
- package/templates/.agents/agents/mobile-developer.md +377 -0
- package/templates/.agents/agents/orchestrator.md +416 -0
- package/templates/.agents/agents/penetration-tester.md +188 -0
- package/templates/.agents/agents/performance-optimizer.md +187 -0
- package/templates/.agents/agents/product-manager.md +112 -0
- package/templates/.agents/agents/product-owner.md +95 -0
- package/templates/.agents/agents/project-planner.md +406 -0
- package/templates/.agents/agents/qa-automation-engineer.md +103 -0
- package/templates/.agents/agents/security-auditor.md +170 -0
- package/templates/.agents/agents/seo-specialist.md +111 -0
- package/templates/.agents/agents/test-engineer.md +158 -0
- package/templates/.agents/rules/GEMINI.md +219 -0
- package/templates/.agents/scripts/auto_preview.py +148 -0
- package/templates/.agents/scripts/checklist.py +217 -0
- package/templates/.agents/scripts/session_manager.py +120 -0
- package/templates/.agents/scripts/verify_all.py +327 -0
- package/templates/.agents/workflows/brainstorm.md +113 -0
- package/templates/.agents/workflows/create.md +59 -0
- package/templates/.agents/workflows/debug.md +103 -0
- package/templates/.agents/workflows/deploy.md +176 -0
- package/templates/.agents/workflows/enhance.md +63 -0
- package/templates/.agents/workflows/orchestrate.md +237 -0
- package/templates/.agents/workflows/plan.md +89 -0
- package/templates/.agents/workflows/preview.md +81 -0
- package/templates/.agents/workflows/setup-brain.md +39 -0
- package/templates/.agents/workflows/status.md +86 -0
- package/templates/.agents/workflows/test.md +144 -0
- package/templates/.agents/workflows/ui-ux-pro-max.md +296 -0
- package/templates/skills_normal/api-patterns/scripts/api_validator.py +211 -0
- package/templates/skills_normal/database-design/scripts/schema_validator.py +172 -0
- package/templates/skills_normal/frontend-design/scripts/accessibility_checker.py +183 -0
- package/templates/skills_normal/frontend-design/scripts/ux_audit.py +722 -0
- package/templates/skills_normal/git-pushing/scripts/smart_commit.sh +19 -0
- package/templates/skills_normal/lint-and-validate/scripts/lint_runner.py +184 -0
- package/templates/skills_normal/lint-and-validate/scripts/type_coverage.py +173 -0
- package/templates/skills_normal/performance-profiling/scripts/lighthouse_audit.py +76 -0
- package/templates/skills_normal/senior-fullstack/scripts/code_quality_analyzer.py +114 -0
- package/templates/skills_normal/senior-fullstack/scripts/fullstack_scaffolder.py +114 -0
- package/templates/skills_normal/senior-fullstack/scripts/project_scaffolder.py +114 -0
- package/templates/skills_normal/seo-fundamentals/scripts/seo_checker.py +219 -0
- package/templates/skills_normal/testing-patterns/scripts/test_runner.py +219 -0
- package/templates/skills_normal/vulnerability-scanner/scripts/security_scan.py +458 -0
- package/templates/vault/007/scripts/config.py +472 -0
- package/templates/vault/007/scripts/full_audit.py +1306 -0
- package/templates/vault/007/scripts/quick_scan.py +481 -0
- package/templates/vault/007/scripts/requirements.txt +26 -0
- package/templates/vault/007/scripts/scanners/__init__.py +0 -0
- package/templates/vault/007/scripts/scanners/dependency_scanner.py +1305 -0
- package/templates/vault/007/scripts/scanners/injection_scanner.py +1104 -0
- package/templates/vault/007/scripts/scanners/secrets_scanner.py +1008 -0
- package/templates/vault/007/scripts/score_calculator.py +693 -0
- package/templates/vault/agent-orchestrator/scripts/match_skills.py +329 -0
- package/templates/vault/agent-orchestrator/scripts/orchestrate.py +304 -0
- package/templates/vault/agent-orchestrator/scripts/requirements.txt +1 -0
- package/templates/vault/agent-orchestrator/scripts/scan_registry.py +508 -0
- package/templates/vault/ai-studio-image/scripts/config.py +613 -0
- package/templates/vault/ai-studio-image/scripts/generate.py +630 -0
- package/templates/vault/ai-studio-image/scripts/prompt_engine.py +424 -0
- package/templates/vault/ai-studio-image/scripts/requirements.txt +4 -0
- package/templates/vault/ai-studio-image/scripts/templates.py +349 -0
- package/templates/vault/android_ui_verification/scripts/verify_ui.sh +32 -0
- package/templates/vault/apify-audience-analysis/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-brand-reputation-monitoring/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-competitor-intelligence/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-content-analytics/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-ecommerce/reference/scripts/package.json +3 -0
- package/templates/vault/apify-ecommerce/reference/scripts/run_actor.js +369 -0
- package/templates/vault/apify-influencer-discovery/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-lead-generation/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-market-research/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-trend-analysis/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-ultimate-scraper/reference/scripts/run_actor.js +363 -0
- package/templates/vault/audio-transcriber/scripts/install-requirements.sh +190 -0
- package/templates/vault/audio-transcriber/scripts/transcribe.py +486 -0
- package/templates/vault/claude-monitor/scripts/api_bench.py +240 -0
- package/templates/vault/claude-monitor/scripts/config.py +69 -0
- package/templates/vault/claude-monitor/scripts/health_check.py +362 -0
- package/templates/vault/claude-monitor/scripts/monitor.py +296 -0
- package/templates/vault/content-creator/scripts/brand_voice_analyzer.py +185 -0
- package/templates/vault/content-creator/scripts/seo_optimizer.py +419 -0
- package/templates/vault/context-agent/scripts/active_context.py +227 -0
- package/templates/vault/context-agent/scripts/compressor.py +149 -0
- package/templates/vault/context-agent/scripts/config.py +69 -0
- package/templates/vault/context-agent/scripts/context_loader.py +155 -0
- package/templates/vault/context-agent/scripts/context_manager.py +302 -0
- package/templates/vault/context-agent/scripts/models.py +103 -0
- package/templates/vault/context-agent/scripts/project_registry.py +132 -0
- package/templates/vault/context-agent/scripts/requirements.txt +6 -0
- package/templates/vault/context-agent/scripts/search.py +115 -0
- package/templates/vault/context-agent/scripts/session_parser.py +206 -0
- package/templates/vault/context-agent/scripts/session_summary.py +319 -0
- package/templates/vault/context-guardian/scripts/context_snapshot.py +229 -0
- package/templates/vault/docx/ooxml/scripts/pack.py +159 -0
- package/templates/vault/docx/ooxml/scripts/unpack.py +29 -0
- package/templates/vault/docx/ooxml/scripts/validate.py +69 -0
- package/templates/vault/docx/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/vault/docx/ooxml/scripts/validation/base.py +951 -0
- package/templates/vault/docx/ooxml/scripts/validation/docx.py +274 -0
- package/templates/vault/docx/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/vault/docx/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/vault/docx/scripts/__init__.py +1 -0
- package/templates/vault/docx/scripts/document.py +1276 -0
- package/templates/vault/docx/scripts/templates/comments.xml +3 -0
- package/templates/vault/docx/scripts/templates/commentsExtended.xml +3 -0
- package/templates/vault/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/templates/vault/docx/scripts/templates/commentsIds.xml +3 -0
- package/templates/vault/docx/scripts/templates/people.xml +3 -0
- package/templates/vault/docx/scripts/utilities.py +374 -0
- package/templates/vault/docx-official/ooxml/scripts/pack.py +159 -0
- package/templates/vault/docx-official/ooxml/scripts/unpack.py +29 -0
- package/templates/vault/docx-official/ooxml/scripts/validate.py +69 -0
- package/templates/vault/docx-official/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/vault/docx-official/ooxml/scripts/validation/base.py +951 -0
- package/templates/vault/docx-official/ooxml/scripts/validation/docx.py +274 -0
- package/templates/vault/docx-official/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/vault/docx-official/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/vault/docx-official/scripts/__init__.py +1 -0
- package/templates/vault/docx-official/scripts/document.py +1276 -0
- package/templates/vault/docx-official/scripts/templates/comments.xml +3 -0
- package/templates/vault/docx-official/scripts/templates/commentsExtended.xml +3 -0
- package/templates/vault/docx-official/scripts/templates/commentsExtensible.xml +3 -0
- package/templates/vault/docx-official/scripts/templates/commentsIds.xml +3 -0
- package/templates/vault/docx-official/scripts/templates/people.xml +3 -0
- package/templates/vault/docx-official/scripts/utilities.py +374 -0
- package/templates/vault/geo-fundamentals/scripts/geo_checker.py +289 -0
- package/templates/vault/helm-chart-scaffolding/scripts/validate-chart.sh +244 -0
- package/templates/vault/i18n-localization/scripts/i18n_checker.py +241 -0
- package/templates/vault/instagram/scripts/account_setup.py +233 -0
- package/templates/vault/instagram/scripts/analyze.py +221 -0
- package/templates/vault/instagram/scripts/api_client.py +444 -0
- package/templates/vault/instagram/scripts/auth.py +411 -0
- package/templates/vault/instagram/scripts/comments.py +160 -0
- package/templates/vault/instagram/scripts/config.py +111 -0
- package/templates/vault/instagram/scripts/db.py +467 -0
- package/templates/vault/instagram/scripts/export.py +138 -0
- package/templates/vault/instagram/scripts/governance.py +233 -0
- package/templates/vault/instagram/scripts/hashtags.py +114 -0
- package/templates/vault/instagram/scripts/insights.py +170 -0
- package/templates/vault/instagram/scripts/media.py +65 -0
- package/templates/vault/instagram/scripts/messages.py +103 -0
- package/templates/vault/instagram/scripts/profile.py +58 -0
- package/templates/vault/instagram/scripts/publish.py +449 -0
- package/templates/vault/instagram/scripts/requirements.txt +5 -0
- package/templates/vault/instagram/scripts/run_all.py +189 -0
- package/templates/vault/instagram/scripts/schedule.py +189 -0
- package/templates/vault/instagram/scripts/serve_api.py +234 -0
- package/templates/vault/instagram/scripts/templates.py +155 -0
- package/templates/vault/junta-leiloeiros/scripts/db.py +216 -0
- package/templates/vault/junta-leiloeiros/scripts/export.py +137 -0
- package/templates/vault/junta-leiloeiros/scripts/requirements.txt +15 -0
- package/templates/vault/junta-leiloeiros/scripts/run_all.py +190 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/__init__.py +4 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/base_scraper.py +209 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/generic_scraper.py +110 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucap.py +110 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/juceac.py +72 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/juceal.py +72 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/juceb.py +68 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucec.py +63 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucema.py +211 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucemg.py +218 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucep.py +70 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucepa.py +74 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucepar.py +80 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucepe.py +78 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucepi.py +69 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucer.py +256 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucerja.py +170 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucern.py +71 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucesc.py +89 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucesp.py +233 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucetins.py +134 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucis_df.py +63 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucisrs.py +299 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/states.py +99 -0
- package/templates/vault/junta-leiloeiros/scripts/serve_api.py +164 -0
- package/templates/vault/junta-leiloeiros/scripts/web_scraper_fallback.py +233 -0
- package/templates/vault/last30days/scripts/last30days.py +521 -0
- package/templates/vault/last30days/scripts/lib/__init__.py +1 -0
- package/templates/vault/last30days/scripts/lib/cache.py +152 -0
- package/templates/vault/last30days/scripts/lib/dates.py +124 -0
- package/templates/vault/last30days/scripts/lib/dedupe.py +120 -0
- package/templates/vault/last30days/scripts/lib/env.py +149 -0
- package/templates/vault/last30days/scripts/lib/http.py +152 -0
- package/templates/vault/last30days/scripts/lib/models.py +175 -0
- package/templates/vault/last30days/scripts/lib/normalize.py +160 -0
- package/templates/vault/last30days/scripts/lib/openai_reddit.py +230 -0
- package/templates/vault/last30days/scripts/lib/reddit_enrich.py +232 -0
- package/templates/vault/last30days/scripts/lib/render.py +383 -0
- package/templates/vault/last30days/scripts/lib/schema.py +336 -0
- package/templates/vault/last30days/scripts/lib/score.py +311 -0
- package/templates/vault/last30days/scripts/lib/ui.py +324 -0
- package/templates/vault/last30days/scripts/lib/websearch.py +401 -0
- package/templates/vault/last30days/scripts/lib/xai_x.py +217 -0
- package/templates/vault/leiloeiro-avaliacao/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-avaliacao/scripts/requirements.txt +1 -0
- package/templates/vault/leiloeiro-edital/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-edital/scripts/requirements.txt +1 -0
- package/templates/vault/leiloeiro-ia/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-ia/scripts/requirements.txt +1 -0
- package/templates/vault/leiloeiro-juridico/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-juridico/scripts/requirements.txt +1 -0
- package/templates/vault/leiloeiro-mercado/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-mercado/scripts/requirements.txt +1 -0
- package/templates/vault/leiloeiro-risco/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-risco/scripts/requirements.txt +1 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/database.ts +24 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/db.ts +35 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/index.ts +2 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/migrations.ts +31 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/schema.sql +8 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/index.ts +44 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/routes/todos.ts +155 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/types/index.ts +35 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/App.css +384 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/App.tsx +81 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/api/todos.ts +57 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/ConfirmDialog.tsx +26 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/EmptyState.tsx +8 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoForm.tsx +43 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoItem.tsx +36 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoList.tsx +27 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/hooks/useTodos.ts +81 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/index.css +48 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/main.tsx +10 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/vite-env.d.ts +1 -0
- package/templates/vault/loki-mode/scripts/export-to-vibe-kanban.sh +178 -0
- package/templates/vault/loki-mode/scripts/loki-wrapper.sh +281 -0
- package/templates/vault/loki-mode/scripts/take-screenshots.js +55 -0
- package/templates/vault/matematico-tao/scripts/complexity_analyzer.py +544 -0
- package/templates/vault/matematico-tao/scripts/dependency_graph.py +538 -0
- package/templates/vault/mcp-builder/scripts/connections.py +151 -0
- package/templates/vault/mcp-builder/scripts/evaluation.py +373 -0
- package/templates/vault/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/templates/vault/mcp-builder/scripts/requirements.txt +2 -0
- package/templates/vault/mobile-design/scripts/mobile_audit.py +670 -0
- package/templates/vault/notebooklm/scripts/__init__.py +81 -0
- package/templates/vault/notebooklm/scripts/ask_question.py +256 -0
- package/templates/vault/notebooklm/scripts/auth_manager.py +358 -0
- package/templates/vault/notebooklm/scripts/browser_session.py +255 -0
- package/templates/vault/notebooklm/scripts/browser_utils.py +107 -0
- package/templates/vault/notebooklm/scripts/cleanup_manager.py +302 -0
- package/templates/vault/notebooklm/scripts/config.py +44 -0
- package/templates/vault/notebooklm/scripts/notebook_manager.py +410 -0
- package/templates/vault/notebooklm/scripts/run.py +102 -0
- package/templates/vault/notebooklm/scripts/setup_environment.py +204 -0
- package/templates/vault/pdf/scripts/check_bounding_boxes.py +70 -0
- package/templates/vault/pdf/scripts/check_bounding_boxes_test.py +226 -0
- package/templates/vault/pdf/scripts/check_fillable_fields.py +12 -0
- package/templates/vault/pdf/scripts/convert_pdf_to_images.py +35 -0
- package/templates/vault/pdf/scripts/create_validation_image.py +41 -0
- package/templates/vault/pdf/scripts/extract_form_field_info.py +152 -0
- package/templates/vault/pdf/scripts/fill_fillable_fields.py +114 -0
- package/templates/vault/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
- package/templates/vault/pdf-official/scripts/check_bounding_boxes.py +70 -0
- package/templates/vault/pdf-official/scripts/check_bounding_boxes_test.py +226 -0
- package/templates/vault/pdf-official/scripts/check_fillable_fields.py +12 -0
- package/templates/vault/pdf-official/scripts/convert_pdf_to_images.py +35 -0
- package/templates/vault/pdf-official/scripts/create_validation_image.py +41 -0
- package/templates/vault/pdf-official/scripts/extract_form_field_info.py +152 -0
- package/templates/vault/pdf-official/scripts/fill_fillable_fields.py +114 -0
- package/templates/vault/pdf-official/scripts/fill_pdf_form_with_annotations.py +108 -0
- package/templates/vault/planning-with-files/scripts/check-complete.sh +44 -0
- package/templates/vault/planning-with-files/scripts/init-session.sh +120 -0
- package/templates/vault/pptx/ooxml/scripts/pack.py +159 -0
- package/templates/vault/pptx/ooxml/scripts/unpack.py +29 -0
- package/templates/vault/pptx/ooxml/scripts/validate.py +69 -0
- package/templates/vault/pptx/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/vault/pptx/ooxml/scripts/validation/base.py +951 -0
- package/templates/vault/pptx/ooxml/scripts/validation/docx.py +274 -0
- package/templates/vault/pptx/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/vault/pptx/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/vault/pptx/scripts/html2pptx.js +979 -0
- package/templates/vault/pptx/scripts/inventory.py +1020 -0
- package/templates/vault/pptx/scripts/rearrange.py +231 -0
- package/templates/vault/pptx/scripts/replace.py +385 -0
- package/templates/vault/pptx/scripts/thumbnail.py +450 -0
- package/templates/vault/pptx-official/ooxml/scripts/pack.py +159 -0
- package/templates/vault/pptx-official/ooxml/scripts/unpack.py +29 -0
- package/templates/vault/pptx-official/ooxml/scripts/validate.py +69 -0
- package/templates/vault/pptx-official/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/vault/pptx-official/ooxml/scripts/validation/base.py +951 -0
- package/templates/vault/pptx-official/ooxml/scripts/validation/docx.py +274 -0
- package/templates/vault/pptx-official/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/vault/pptx-official/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/vault/pptx-official/scripts/html2pptx.js +979 -0
- package/templates/vault/pptx-official/scripts/inventory.py +1020 -0
- package/templates/vault/pptx-official/scripts/rearrange.py +231 -0
- package/templates/vault/pptx-official/scripts/replace.py +385 -0
- package/templates/vault/pptx-official/scripts/thumbnail.py +450 -0
- package/templates/vault/product-manager-toolkit/scripts/customer_interview_analyzer.py +441 -0
- package/templates/vault/product-manager-toolkit/scripts/rice_prioritizer.py +296 -0
- package/templates/vault/prompt-engineering-patterns/scripts/optimize-prompt.py +279 -0
- package/templates/vault/scripts/.skill_cache.json +7538 -0
- package/templates/vault/scripts/skill_search.py +228 -0
- package/templates/vault/senior-architect/scripts/architecture_diagram_generator.py +114 -0
- package/templates/vault/senior-architect/scripts/dependency_analyzer.py +114 -0
- package/templates/vault/senior-architect/scripts/project_architect.py +114 -0
- package/templates/vault/shopify-development/scripts/requirements.txt +19 -0
- package/templates/vault/shopify-development/scripts/shopify_graphql.py +428 -0
- package/templates/vault/shopify-development/scripts/shopify_init.py +441 -0
- package/templates/vault/shopify-development/scripts/tests/test_shopify_init.py +379 -0
- package/templates/vault/skill-creator/scripts/init_skill.py +303 -0
- package/templates/vault/skill-creator/scripts/package_skill.py +110 -0
- package/templates/vault/skill-creator/scripts/quick_validate.py +95 -0
- package/templates/vault/skill-installer/scripts/detect_skills.py +318 -0
- package/templates/vault/skill-installer/scripts/install_skill.py +1708 -0
- package/templates/vault/skill-installer/scripts/package_skill.py +417 -0
- package/templates/vault/skill-installer/scripts/requirements.txt +1 -0
- package/templates/vault/skill-installer/scripts/validate_skill.py +430 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/__init__.py +13 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/code_quality.py +247 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/cross_skill.py +134 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/dependencies.py +121 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/documentation.py +189 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/governance_audit.py +153 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/performance.py +164 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/security.py +189 -0
- package/templates/vault/skill-sentinel/scripts/config.py +158 -0
- package/templates/vault/skill-sentinel/scripts/cost_optimizer.py +146 -0
- package/templates/vault/skill-sentinel/scripts/db.py +354 -0
- package/templates/vault/skill-sentinel/scripts/governance.py +58 -0
- package/templates/vault/skill-sentinel/scripts/recommender.py +228 -0
- package/templates/vault/skill-sentinel/scripts/report_generator.py +224 -0
- package/templates/vault/skill-sentinel/scripts/requirements.txt +1 -0
- package/templates/vault/skill-sentinel/scripts/run_audit.py +290 -0
- package/templates/vault/skill-sentinel/scripts/scanner.py +271 -0
- package/templates/vault/stability-ai/scripts/config.py +266 -0
- package/templates/vault/stability-ai/scripts/generate.py +687 -0
- package/templates/vault/stability-ai/scripts/requirements.txt +4 -0
- package/templates/vault/stability-ai/scripts/styles.py +174 -0
- package/templates/vault/telegram/assets/boilerplate/nodejs/src/bot-client.ts +86 -0
- package/templates/vault/telegram/assets/boilerplate/nodejs/src/handlers.ts +79 -0
- package/templates/vault/telegram/assets/boilerplate/nodejs/src/index.ts +32 -0
- package/templates/vault/telegram/scripts/send_message.py +143 -0
- package/templates/vault/telegram/scripts/setup_project.py +103 -0
- package/templates/vault/telegram/scripts/test_bot.py +144 -0
- package/templates/vault/typescript-expert/scripts/ts_diagnostic.py +203 -0
- package/templates/vault/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
- package/templates/vault/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
- package/templates/vault/ui-ux-pro-max/scripts/core.py +257 -0
- package/templates/vault/ui-ux-pro-max/scripts/design_system.py +487 -0
- package/templates/vault/ui-ux-pro-max/scripts/search.py +76 -0
- package/templates/vault/videodb/scripts/ws_listener.py +204 -0
- package/templates/vault/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
- package/templates/vault/web-artifacts-builder/scripts/init-artifact.sh +322 -0
- package/templates/vault/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
- package/templates/vault/webapp-testing/scripts/with_server.py +106 -0
- package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/index.ts +125 -0
- package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/template-manager.ts +67 -0
- package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/types.ts +216 -0
- package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/webhook-handler.ts +173 -0
- package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/whatsapp-client.ts +193 -0
- package/templates/vault/whatsapp-cloud-api/scripts/send_test_message.py +137 -0
- package/templates/vault/whatsapp-cloud-api/scripts/setup_project.py +118 -0
- package/templates/vault/whatsapp-cloud-api/scripts/validate_config.py +190 -0
- package/templates/vault/youtube-summarizer/scripts/extract-transcript.py +65 -0
- package/templates/vault/youtube-summarizer/scripts/install-dependencies.sh +28 -0
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Camada de persistência SQLite para a skill Instagram.
|
|
3
|
+
|
|
4
|
+
Uso:
|
|
5
|
+
from db import Database
|
|
6
|
+
db = Database()
|
|
7
|
+
db.init()
|
|
8
|
+
db.upsert_account({...})
|
|
9
|
+
db.insert_post({...})
|
|
10
|
+
stats = db.get_stats()
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import sqlite3
|
|
16
|
+
from datetime import datetime, timezone
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any, Dict, List, Optional
|
|
19
|
+
|
|
20
|
+
from config import DB_PATH
|
|
21
|
+
|
|
22
|
+
DDL = """
|
|
23
|
+
-- Contas Instagram (multi-conta ready)
|
|
24
|
+
CREATE TABLE IF NOT EXISTS accounts (
|
|
25
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
26
|
+
ig_user_id TEXT UNIQUE NOT NULL,
|
|
27
|
+
username TEXT,
|
|
28
|
+
account_type TEXT,
|
|
29
|
+
access_token TEXT NOT NULL,
|
|
30
|
+
token_expires_at TEXT,
|
|
31
|
+
facebook_page_id TEXT,
|
|
32
|
+
app_id TEXT,
|
|
33
|
+
app_secret TEXT,
|
|
34
|
+
is_active INTEGER DEFAULT 1,
|
|
35
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
-- Pipeline de conteúdo: draft → approved → scheduled → container_created → published | failed
|
|
39
|
+
CREATE TABLE IF NOT EXISTS posts (
|
|
40
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
41
|
+
account_id INTEGER REFERENCES accounts(id),
|
|
42
|
+
media_type TEXT,
|
|
43
|
+
media_url TEXT,
|
|
44
|
+
local_path TEXT,
|
|
45
|
+
caption TEXT,
|
|
46
|
+
hashtags TEXT,
|
|
47
|
+
template_id INTEGER REFERENCES templates(id),
|
|
48
|
+
status TEXT DEFAULT 'draft',
|
|
49
|
+
scheduled_at TEXT,
|
|
50
|
+
published_at TEXT,
|
|
51
|
+
ig_media_id TEXT,
|
|
52
|
+
ig_container_id TEXT,
|
|
53
|
+
permalink TEXT,
|
|
54
|
+
error_msg TEXT,
|
|
55
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
CREATE TABLE IF NOT EXISTS comments (
|
|
59
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
60
|
+
account_id INTEGER REFERENCES accounts(id),
|
|
61
|
+
ig_comment_id TEXT UNIQUE,
|
|
62
|
+
ig_media_id TEXT,
|
|
63
|
+
username TEXT,
|
|
64
|
+
text TEXT,
|
|
65
|
+
timestamp TEXT,
|
|
66
|
+
replied INTEGER DEFAULT 0,
|
|
67
|
+
reply_text TEXT,
|
|
68
|
+
hidden INTEGER DEFAULT 0,
|
|
69
|
+
fetched_at TEXT DEFAULT (datetime('now'))
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
CREATE TABLE IF NOT EXISTS insights (
|
|
73
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
74
|
+
account_id INTEGER REFERENCES accounts(id),
|
|
75
|
+
ig_media_id TEXT,
|
|
76
|
+
metric_name TEXT,
|
|
77
|
+
metric_value REAL,
|
|
78
|
+
period TEXT,
|
|
79
|
+
fetched_at TEXT DEFAULT (datetime('now')),
|
|
80
|
+
raw_json TEXT
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
CREATE TABLE IF NOT EXISTS user_insights (
|
|
84
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
85
|
+
account_id INTEGER REFERENCES accounts(id),
|
|
86
|
+
metric_name TEXT,
|
|
87
|
+
metric_value REAL,
|
|
88
|
+
period TEXT,
|
|
89
|
+
end_time TEXT,
|
|
90
|
+
fetched_at TEXT DEFAULT (datetime('now'))
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
CREATE TABLE IF NOT EXISTS templates (
|
|
94
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
95
|
+
name TEXT UNIQUE NOT NULL,
|
|
96
|
+
caption_template TEXT,
|
|
97
|
+
hashtag_set TEXT,
|
|
98
|
+
default_schedule_time TEXT,
|
|
99
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
CREATE TABLE IF NOT EXISTS hashtag_searches (
|
|
103
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
104
|
+
account_id INTEGER REFERENCES accounts(id),
|
|
105
|
+
hashtag TEXT,
|
|
106
|
+
ig_hashtag_id TEXT,
|
|
107
|
+
searched_at TEXT DEFAULT (datetime('now'))
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
CREATE TABLE IF NOT EXISTS action_log (
|
|
111
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
112
|
+
account_id INTEGER,
|
|
113
|
+
action TEXT NOT NULL,
|
|
114
|
+
params TEXT,
|
|
115
|
+
result TEXT,
|
|
116
|
+
confirmed INTEGER,
|
|
117
|
+
rate_remaining TEXT,
|
|
118
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
-- Índices
|
|
122
|
+
CREATE INDEX IF NOT EXISTS idx_posts_status ON posts (status);
|
|
123
|
+
CREATE INDEX IF NOT EXISTS idx_posts_account ON posts (account_id);
|
|
124
|
+
CREATE INDEX IF NOT EXISTS idx_posts_scheduled ON posts (scheduled_at);
|
|
125
|
+
CREATE INDEX IF NOT EXISTS idx_comments_media ON comments (ig_media_id);
|
|
126
|
+
CREATE INDEX IF NOT EXISTS idx_comments_account ON comments (account_id);
|
|
127
|
+
CREATE INDEX IF NOT EXISTS idx_insights_media ON insights (ig_media_id);
|
|
128
|
+
CREATE INDEX IF NOT EXISTS idx_insights_account ON insights (account_id);
|
|
129
|
+
CREATE INDEX IF NOT EXISTS idx_user_insights_acct ON user_insights (account_id);
|
|
130
|
+
CREATE INDEX IF NOT EXISTS idx_action_log_action ON action_log (action);
|
|
131
|
+
CREATE INDEX IF NOT EXISTS idx_action_log_time ON action_log (created_at);
|
|
132
|
+
CREATE INDEX IF NOT EXISTS idx_hashtag_searched ON hashtag_searches (searched_at);
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
_POSTS_COLUMNS = frozenset({
|
|
137
|
+
"account_id", "media_type", "media_url", "local_path", "caption",
|
|
138
|
+
"hashtags", "template_id", "status", "scheduled_at", "published_at",
|
|
139
|
+
"ig_media_id", "ig_container_id", "permalink", "error_msg", "created_at",
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class Database:
|
|
144
|
+
def __init__(self, db_path: Path = DB_PATH):
|
|
145
|
+
self.db_path = Path(db_path)
|
|
146
|
+
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
147
|
+
|
|
148
|
+
def _connect(self) -> sqlite3.Connection:
|
|
149
|
+
conn = sqlite3.connect(self.db_path)
|
|
150
|
+
conn.row_factory = sqlite3.Row
|
|
151
|
+
conn.execute("PRAGMA journal_mode=WAL")
|
|
152
|
+
conn.execute("PRAGMA synchronous=NORMAL")
|
|
153
|
+
conn.execute("PRAGMA foreign_keys=ON")
|
|
154
|
+
return conn
|
|
155
|
+
|
|
156
|
+
def init(self) -> None:
|
|
157
|
+
"""Cria tabelas e índices se não existirem."""
|
|
158
|
+
with self._connect() as conn:
|
|
159
|
+
conn.executescript(DDL)
|
|
160
|
+
|
|
161
|
+
# ── Accounts ──────────────────────────────────────────────────────────────
|
|
162
|
+
|
|
163
|
+
def upsert_account(self, data: Dict[str, Any]) -> int:
|
|
164
|
+
"""Insere ou atualiza conta. Retorna o id da conta."""
|
|
165
|
+
sql = """
|
|
166
|
+
INSERT INTO accounts (ig_user_id, username, account_type, access_token,
|
|
167
|
+
token_expires_at, facebook_page_id, app_id, app_secret)
|
|
168
|
+
VALUES (:ig_user_id, :username, :account_type, :access_token,
|
|
169
|
+
:token_expires_at, :facebook_page_id, :app_id, :app_secret)
|
|
170
|
+
ON CONFLICT(ig_user_id) DO UPDATE SET
|
|
171
|
+
username = excluded.username,
|
|
172
|
+
account_type = excluded.account_type,
|
|
173
|
+
access_token = excluded.access_token,
|
|
174
|
+
token_expires_at = excluded.token_expires_at,
|
|
175
|
+
facebook_page_id = excluded.facebook_page_id,
|
|
176
|
+
app_id = excluded.app_id,
|
|
177
|
+
app_secret = excluded.app_secret,
|
|
178
|
+
is_active = 1
|
|
179
|
+
"""
|
|
180
|
+
with self._connect() as conn:
|
|
181
|
+
conn.execute(sql, data)
|
|
182
|
+
row = conn.execute(
|
|
183
|
+
"SELECT id FROM accounts WHERE ig_user_id = ?",
|
|
184
|
+
[data["ig_user_id"]]
|
|
185
|
+
).fetchone()
|
|
186
|
+
return row["id"]
|
|
187
|
+
|
|
188
|
+
def get_active_account(self) -> Optional[Dict[str, Any]]:
|
|
189
|
+
"""Retorna a conta ativa (primeira ativa encontrada)."""
|
|
190
|
+
with self._connect() as conn:
|
|
191
|
+
row = conn.execute(
|
|
192
|
+
"SELECT * FROM accounts WHERE is_active = 1 ORDER BY id LIMIT 1"
|
|
193
|
+
).fetchone()
|
|
194
|
+
return dict(row) if row else None
|
|
195
|
+
|
|
196
|
+
def get_account_by_id(self, account_id: int) -> Optional[Dict[str, Any]]:
|
|
197
|
+
with self._connect() as conn:
|
|
198
|
+
row = conn.execute(
|
|
199
|
+
"SELECT * FROM accounts WHERE id = ?", [account_id]
|
|
200
|
+
).fetchone()
|
|
201
|
+
return dict(row) if row else None
|
|
202
|
+
|
|
203
|
+
def update_token(self, account_id: int, access_token: str, expires_at: str) -> None:
|
|
204
|
+
with self._connect() as conn:
|
|
205
|
+
conn.execute(
|
|
206
|
+
"UPDATE accounts SET access_token = ?, token_expires_at = ? WHERE id = ?",
|
|
207
|
+
[access_token, expires_at, account_id],
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# ── Posts (Pipeline) ──────────────────────────────────────────────────────
|
|
211
|
+
|
|
212
|
+
def insert_post(self, data: Dict[str, Any]) -> int:
|
|
213
|
+
"""Cria um novo post (draft por padrão). Retorna o id."""
|
|
214
|
+
keys = [k for k in data.keys() if k != "id" and k in _POSTS_COLUMNS]
|
|
215
|
+
if not keys:
|
|
216
|
+
raise ValueError("No valid columns provided for insert_post")
|
|
217
|
+
placeholders = ", ".join("?" for _ in keys)
|
|
218
|
+
columns = ", ".join(keys)
|
|
219
|
+
values = [data[k] for k in keys]
|
|
220
|
+
sql = f"INSERT INTO posts ({columns}) VALUES ({placeholders})"
|
|
221
|
+
with self._connect() as conn:
|
|
222
|
+
cursor = conn.execute(sql, values)
|
|
223
|
+
return cursor.lastrowid
|
|
224
|
+
|
|
225
|
+
def update_post_status(self, post_id: int, status: str, **extra) -> None:
|
|
226
|
+
"""Atualiza status de um post e campos adicionais."""
|
|
227
|
+
sets = ["status = ?"]
|
|
228
|
+
params: list = [status]
|
|
229
|
+
for k, v in extra.items():
|
|
230
|
+
if k not in _POSTS_COLUMNS:
|
|
231
|
+
raise ValueError(f"Invalid column name for update_post_status: {k}")
|
|
232
|
+
sets.append(f"{k} = ?")
|
|
233
|
+
params.append(v)
|
|
234
|
+
params.append(post_id)
|
|
235
|
+
sql = f"UPDATE posts SET {', '.join(sets)} WHERE id = ?"
|
|
236
|
+
with self._connect() as conn:
|
|
237
|
+
conn.execute(sql, params)
|
|
238
|
+
|
|
239
|
+
def get_posts(
|
|
240
|
+
self,
|
|
241
|
+
account_id: Optional[int] = None,
|
|
242
|
+
status: Optional[str] = None,
|
|
243
|
+
limit: int = 50,
|
|
244
|
+
offset: int = 0,
|
|
245
|
+
) -> List[Dict[str, Any]]:
|
|
246
|
+
conditions = []
|
|
247
|
+
params: list = []
|
|
248
|
+
if account_id:
|
|
249
|
+
conditions.append("account_id = ?")
|
|
250
|
+
params.append(account_id)
|
|
251
|
+
if status:
|
|
252
|
+
conditions.append("status = ?")
|
|
253
|
+
params.append(status)
|
|
254
|
+
where = f"WHERE {' AND '.join(conditions)}" if conditions else ""
|
|
255
|
+
sql = f"SELECT * FROM posts {where} ORDER BY created_at DESC LIMIT ? OFFSET ?"
|
|
256
|
+
params.extend([limit, offset])
|
|
257
|
+
with self._connect() as conn:
|
|
258
|
+
rows = conn.execute(sql, params).fetchall()
|
|
259
|
+
return [dict(r) for r in rows]
|
|
260
|
+
|
|
261
|
+
def get_posts_for_publishing(self, account_id: int) -> List[Dict[str, Any]]:
|
|
262
|
+
"""Posts aprovados/agendados prontos para publicar."""
|
|
263
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
264
|
+
sql = """
|
|
265
|
+
SELECT * FROM posts
|
|
266
|
+
WHERE account_id = ? AND (
|
|
267
|
+
status = 'approved'
|
|
268
|
+
OR (status = 'scheduled' AND scheduled_at <= ?)
|
|
269
|
+
OR status = 'container_created'
|
|
270
|
+
)
|
|
271
|
+
ORDER BY scheduled_at ASC, created_at ASC
|
|
272
|
+
"""
|
|
273
|
+
with self._connect() as conn:
|
|
274
|
+
rows = conn.execute(sql, [account_id, now]).fetchall()
|
|
275
|
+
return [dict(r) for r in rows]
|
|
276
|
+
|
|
277
|
+
def get_post_by_id(self, post_id: int) -> Optional[Dict[str, Any]]:
|
|
278
|
+
with self._connect() as conn:
|
|
279
|
+
row = conn.execute("SELECT * FROM posts WHERE id = ?", [post_id]).fetchone()
|
|
280
|
+
return dict(row) if row else None
|
|
281
|
+
|
|
282
|
+
# ── Comments ──────────────────────────────────────────────────────────────
|
|
283
|
+
|
|
284
|
+
def upsert_comments(self, comments: List[Dict[str, Any]]) -> int:
|
|
285
|
+
sql = """
|
|
286
|
+
INSERT INTO comments (account_id, ig_comment_id, ig_media_id, username, text, timestamp)
|
|
287
|
+
VALUES (:account_id, :ig_comment_id, :ig_media_id, :username, :text, :timestamp)
|
|
288
|
+
ON CONFLICT(ig_comment_id) DO UPDATE SET
|
|
289
|
+
text = excluded.text,
|
|
290
|
+
timestamp = excluded.timestamp
|
|
291
|
+
"""
|
|
292
|
+
with self._connect() as conn:
|
|
293
|
+
conn.executemany(sql, comments)
|
|
294
|
+
return len(comments)
|
|
295
|
+
|
|
296
|
+
def get_comments(
|
|
297
|
+
self,
|
|
298
|
+
ig_media_id: Optional[str] = None,
|
|
299
|
+
account_id: Optional[int] = None,
|
|
300
|
+
unreplied_only: bool = False,
|
|
301
|
+
limit: int = 50,
|
|
302
|
+
) -> List[Dict[str, Any]]:
|
|
303
|
+
conditions = []
|
|
304
|
+
params: list = []
|
|
305
|
+
if ig_media_id:
|
|
306
|
+
conditions.append("ig_media_id = ?")
|
|
307
|
+
params.append(ig_media_id)
|
|
308
|
+
if account_id:
|
|
309
|
+
conditions.append("account_id = ?")
|
|
310
|
+
params.append(account_id)
|
|
311
|
+
if unreplied_only:
|
|
312
|
+
conditions.append("replied = 0")
|
|
313
|
+
where = f"WHERE {' AND '.join(conditions)}" if conditions else ""
|
|
314
|
+
sql = f"SELECT * FROM comments {where} ORDER BY timestamp DESC LIMIT ?"
|
|
315
|
+
params.append(limit)
|
|
316
|
+
with self._connect() as conn:
|
|
317
|
+
rows = conn.execute(sql, params).fetchall()
|
|
318
|
+
return [dict(r) for r in rows]
|
|
319
|
+
|
|
320
|
+
# ── Insights ──────────────────────────────────────────────────────────────
|
|
321
|
+
|
|
322
|
+
def insert_insights(self, records: List[Dict[str, Any]]) -> int:
|
|
323
|
+
sql = """
|
|
324
|
+
INSERT INTO insights (account_id, ig_media_id, metric_name, metric_value, period, raw_json)
|
|
325
|
+
VALUES (:account_id, :ig_media_id, :metric_name, :metric_value, :period, :raw_json)
|
|
326
|
+
"""
|
|
327
|
+
with self._connect() as conn:
|
|
328
|
+
conn.executemany(sql, records)
|
|
329
|
+
return len(records)
|
|
330
|
+
|
|
331
|
+
def insert_user_insights(self, records: List[Dict[str, Any]]) -> int:
|
|
332
|
+
sql = """
|
|
333
|
+
INSERT INTO user_insights (account_id, metric_name, metric_value, period, end_time)
|
|
334
|
+
VALUES (:account_id, :metric_name, :metric_value, :period, :end_time)
|
|
335
|
+
"""
|
|
336
|
+
with self._connect() as conn:
|
|
337
|
+
conn.executemany(sql, records)
|
|
338
|
+
return len(records)
|
|
339
|
+
|
|
340
|
+
# ── Templates ─────────────────────────────────────────────────────────────
|
|
341
|
+
|
|
342
|
+
def upsert_template(self, data: Dict[str, Any]) -> int:
|
|
343
|
+
sql = """
|
|
344
|
+
INSERT INTO templates (name, caption_template, hashtag_set, default_schedule_time)
|
|
345
|
+
VALUES (:name, :caption_template, :hashtag_set, :default_schedule_time)
|
|
346
|
+
ON CONFLICT(name) DO UPDATE SET
|
|
347
|
+
caption_template = excluded.caption_template,
|
|
348
|
+
hashtag_set = excluded.hashtag_set,
|
|
349
|
+
default_schedule_time = excluded.default_schedule_time
|
|
350
|
+
"""
|
|
351
|
+
with self._connect() as conn:
|
|
352
|
+
conn.execute(sql, data)
|
|
353
|
+
row = conn.execute("SELECT id FROM templates WHERE name = ?", [data["name"]]).fetchone()
|
|
354
|
+
return row["id"]
|
|
355
|
+
|
|
356
|
+
def get_templates(self) -> List[Dict[str, Any]]:
|
|
357
|
+
with self._connect() as conn:
|
|
358
|
+
rows = conn.execute("SELECT * FROM templates ORDER BY name").fetchall()
|
|
359
|
+
return [dict(r) for r in rows]
|
|
360
|
+
|
|
361
|
+
def get_template_by_name(self, name: str) -> Optional[Dict[str, Any]]:
|
|
362
|
+
with self._connect() as conn:
|
|
363
|
+
row = conn.execute("SELECT * FROM templates WHERE name = ?", [name]).fetchone()
|
|
364
|
+
return dict(row) if row else None
|
|
365
|
+
|
|
366
|
+
def delete_template(self, name: str) -> bool:
|
|
367
|
+
with self._connect() as conn:
|
|
368
|
+
cursor = conn.execute("DELETE FROM templates WHERE name = ?", [name])
|
|
369
|
+
return cursor.rowcount > 0
|
|
370
|
+
|
|
371
|
+
# ── Hashtag Searches ──────────────────────────────────────────────────────
|
|
372
|
+
|
|
373
|
+
def insert_hashtag_search(self, data: Dict[str, Any]) -> None:
|
|
374
|
+
sql = """
|
|
375
|
+
INSERT INTO hashtag_searches (account_id, hashtag, ig_hashtag_id)
|
|
376
|
+
VALUES (:account_id, :hashtag, :ig_hashtag_id)
|
|
377
|
+
"""
|
|
378
|
+
with self._connect() as conn:
|
|
379
|
+
conn.execute(sql, data)
|
|
380
|
+
|
|
381
|
+
def count_hashtag_searches_last_week(self, account_id: int) -> int:
|
|
382
|
+
sql = """
|
|
383
|
+
SELECT COUNT(DISTINCT hashtag) FROM hashtag_searches
|
|
384
|
+
WHERE account_id = ? AND searched_at >= datetime('now', '-7 days')
|
|
385
|
+
"""
|
|
386
|
+
with self._connect() as conn:
|
|
387
|
+
return conn.execute(sql, [account_id]).fetchone()[0]
|
|
388
|
+
|
|
389
|
+
# ── Action Log ────────────────────────────────────────────────────────────
|
|
390
|
+
|
|
391
|
+
def log_action(self, data: Dict[str, Any]) -> None:
|
|
392
|
+
sql = """
|
|
393
|
+
INSERT INTO action_log (account_id, action, params, result, confirmed, rate_remaining)
|
|
394
|
+
VALUES (:account_id, :action, :params, :result, :confirmed, :rate_remaining)
|
|
395
|
+
"""
|
|
396
|
+
with self._connect() as conn:
|
|
397
|
+
conn.execute(sql, data)
|
|
398
|
+
|
|
399
|
+
def get_recent_actions(self, limit: int = 20, action: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
400
|
+
if action:
|
|
401
|
+
sql = "SELECT * FROM action_log WHERE action = ? ORDER BY created_at DESC LIMIT ?"
|
|
402
|
+
params = [action, limit]
|
|
403
|
+
else:
|
|
404
|
+
sql = "SELECT * FROM action_log ORDER BY created_at DESC LIMIT ?"
|
|
405
|
+
params = [limit]
|
|
406
|
+
with self._connect() as conn:
|
|
407
|
+
rows = conn.execute(sql, params).fetchall()
|
|
408
|
+
return [dict(r) for r in rows]
|
|
409
|
+
|
|
410
|
+
# ── Stats ─────────────────────────────────────────────────────────────────
|
|
411
|
+
|
|
412
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
413
|
+
"""Retorna estatísticas gerais do banco."""
|
|
414
|
+
with self._connect() as conn:
|
|
415
|
+
accounts = conn.execute("SELECT COUNT(*) FROM accounts WHERE is_active = 1").fetchone()[0]
|
|
416
|
+
posts_total = conn.execute("SELECT COUNT(*) FROM posts").fetchone()[0]
|
|
417
|
+
posts_published = conn.execute("SELECT COUNT(*) FROM posts WHERE status = 'published'").fetchone()[0]
|
|
418
|
+
posts_draft = conn.execute("SELECT COUNT(*) FROM posts WHERE status = 'draft'").fetchone()[0]
|
|
419
|
+
posts_scheduled = conn.execute("SELECT COUNT(*) FROM posts WHERE status = 'scheduled'").fetchone()[0]
|
|
420
|
+
comments_total = conn.execute("SELECT COUNT(*) FROM comments").fetchone()[0]
|
|
421
|
+
comments_unreplied = conn.execute("SELECT COUNT(*) FROM comments WHERE replied = 0").fetchone()[0]
|
|
422
|
+
templates = conn.execute("SELECT COUNT(*) FROM templates").fetchone()[0]
|
|
423
|
+
actions_today = conn.execute(
|
|
424
|
+
"SELECT COUNT(*) FROM action_log WHERE created_at >= date('now')"
|
|
425
|
+
).fetchone()[0]
|
|
426
|
+
|
|
427
|
+
return {
|
|
428
|
+
"accounts_active": accounts,
|
|
429
|
+
"posts": {
|
|
430
|
+
"total": posts_total,
|
|
431
|
+
"published": posts_published,
|
|
432
|
+
"draft": posts_draft,
|
|
433
|
+
"scheduled": posts_scheduled,
|
|
434
|
+
},
|
|
435
|
+
"comments": {
|
|
436
|
+
"total": comments_total,
|
|
437
|
+
"unreplied": comments_unreplied,
|
|
438
|
+
},
|
|
439
|
+
"templates": templates,
|
|
440
|
+
"actions_today": actions_today,
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
def count_requests_last_hour(self) -> int:
|
|
444
|
+
"""Conta requests na última hora (para rate limiting)."""
|
|
445
|
+
sql = """
|
|
446
|
+
SELECT COUNT(*) FROM action_log
|
|
447
|
+
WHERE created_at >= datetime('now', '-1 hour')
|
|
448
|
+
"""
|
|
449
|
+
with self._connect() as conn:
|
|
450
|
+
return conn.execute(sql).fetchone()[0]
|
|
451
|
+
|
|
452
|
+
def count_publishes_today(self) -> int:
|
|
453
|
+
"""Conta publicações hoje (para rate limiting)."""
|
|
454
|
+
sql = """
|
|
455
|
+
SELECT COUNT(*) FROM action_log
|
|
456
|
+
WHERE action LIKE 'publish_%' AND created_at >= date('now')
|
|
457
|
+
"""
|
|
458
|
+
with self._connect() as conn:
|
|
459
|
+
return conn.execute(sql).fetchone()[0]
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
# ── CLI rápido para verificação ──────────────────────────────────────────────
|
|
463
|
+
if __name__ == "__main__":
|
|
464
|
+
db = Database()
|
|
465
|
+
db.init()
|
|
466
|
+
stats = db.get_stats()
|
|
467
|
+
print(json.dumps(stats, indent=2, ensure_ascii=False))
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Exportação de dados do Instagram para diferentes formatos.
|
|
3
|
+
|
|
4
|
+
Uso:
|
|
5
|
+
python scripts/export.py --type insights --format csv
|
|
6
|
+
python scripts/export.py --type comments --format json
|
|
7
|
+
python scripts/export.py --type posts --format jsonl
|
|
8
|
+
python scripts/export.py --type all --format csv
|
|
9
|
+
python scripts/export.py --type actions --format json --output /caminho/
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import csv
|
|
15
|
+
import json
|
|
16
|
+
import sys
|
|
17
|
+
from datetime import datetime, timezone
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
21
|
+
|
|
22
|
+
from config import EXPORTS_DIR
|
|
23
|
+
from db import Database
|
|
24
|
+
|
|
25
|
+
db = Database()
|
|
26
|
+
db.init()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def export_json(records: list, output_dir: Path, name: str) -> Path:
|
|
30
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
31
|
+
ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
|
32
|
+
path = output_dir / f"instagram_{name}_{ts}.json"
|
|
33
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
34
|
+
json.dump(
|
|
35
|
+
{"exported_at": datetime.now(timezone.utc).isoformat(), "total": len(records), "data": records},
|
|
36
|
+
f, ensure_ascii=False, indent=2,
|
|
37
|
+
)
|
|
38
|
+
print(f"[JSON] {len(records)} registros ->{path}")
|
|
39
|
+
return path
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def export_jsonl(records: list, output_dir: Path, name: str) -> Path:
|
|
43
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
44
|
+
ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
|
45
|
+
path = output_dir / f"instagram_{name}_{ts}.jsonl"
|
|
46
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
47
|
+
for rec in records:
|
|
48
|
+
f.write(json.dumps(rec, ensure_ascii=False) + "\n")
|
|
49
|
+
print(f"[JSONL] {len(records)} registros ->{path}")
|
|
50
|
+
return path
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def export_csv_file(records: list, output_dir: Path, name: str) -> Path:
|
|
54
|
+
if not records:
|
|
55
|
+
print("[CSV] Nenhum registro para exportar.")
|
|
56
|
+
return None
|
|
57
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
58
|
+
ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
|
59
|
+
path = output_dir / f"instagram_{name}_{ts}.csv"
|
|
60
|
+
with open(path, "w", newline="", encoding="utf-8-sig") as f:
|
|
61
|
+
writer = csv.DictWriter(f, fieldnames=list(records[0].keys()), extrasaction="ignore")
|
|
62
|
+
writer.writeheader()
|
|
63
|
+
writer.writerows(records)
|
|
64
|
+
print(f"[CSV] {len(records)} registros ->{path}")
|
|
65
|
+
return path
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def get_data(data_type: str) -> tuple:
|
|
69
|
+
"""Retorna (records, name) para o tipo de dados."""
|
|
70
|
+
conn = db._connect()
|
|
71
|
+
|
|
72
|
+
if data_type == "posts":
|
|
73
|
+
rows = conn.execute("SELECT * FROM posts ORDER BY created_at DESC").fetchall()
|
|
74
|
+
return [dict(r) for r in rows], "posts"
|
|
75
|
+
elif data_type == "comments":
|
|
76
|
+
rows = conn.execute("SELECT * FROM comments ORDER BY timestamp DESC").fetchall()
|
|
77
|
+
return [dict(r) for r in rows], "comments"
|
|
78
|
+
elif data_type == "insights":
|
|
79
|
+
rows = conn.execute("""
|
|
80
|
+
SELECT i.*, p.caption, p.permalink
|
|
81
|
+
FROM insights i
|
|
82
|
+
LEFT JOIN posts p ON p.ig_media_id = i.ig_media_id
|
|
83
|
+
ORDER BY i.fetched_at DESC
|
|
84
|
+
""").fetchall()
|
|
85
|
+
return [dict(r) for r in rows], "insights"
|
|
86
|
+
elif data_type == "user_insights":
|
|
87
|
+
rows = conn.execute("SELECT * FROM user_insights ORDER BY end_time DESC").fetchall()
|
|
88
|
+
return [dict(r) for r in rows], "user_insights"
|
|
89
|
+
elif data_type == "templates":
|
|
90
|
+
rows = conn.execute("SELECT * FROM templates ORDER BY name").fetchall()
|
|
91
|
+
return [dict(r) for r in rows], "templates"
|
|
92
|
+
elif data_type == "actions":
|
|
93
|
+
rows = conn.execute("SELECT * FROM action_log ORDER BY created_at DESC").fetchall()
|
|
94
|
+
return [dict(r) for r in rows], "actions"
|
|
95
|
+
elif data_type == "all":
|
|
96
|
+
return None, "all"
|
|
97
|
+
else:
|
|
98
|
+
return [], data_type
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def do_export(records: list, name: str, fmt: str, output_dir: Path) -> None:
|
|
102
|
+
if fmt in ("json", "all"):
|
|
103
|
+
export_json(records, output_dir, name)
|
|
104
|
+
if fmt in ("jsonl", "all"):
|
|
105
|
+
export_jsonl(records, output_dir, name)
|
|
106
|
+
if fmt in ("csv", "all"):
|
|
107
|
+
export_csv_file(records, output_dir, name)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def main():
|
|
111
|
+
parser = argparse.ArgumentParser(description="Exportar dados do Instagram")
|
|
112
|
+
parser.add_argument("--type", required=True,
|
|
113
|
+
choices=["posts", "comments", "insights", "user_insights", "templates", "actions", "all"],
|
|
114
|
+
help="Tipo de dados")
|
|
115
|
+
parser.add_argument("--format", default="csv", choices=["json", "jsonl", "csv", "all"],
|
|
116
|
+
help="Formato (default: csv)")
|
|
117
|
+
parser.add_argument("--output", default=str(EXPORTS_DIR), help=f"Diretório (default: {EXPORTS_DIR})")
|
|
118
|
+
args = parser.parse_args()
|
|
119
|
+
|
|
120
|
+
output_dir = Path(args.output)
|
|
121
|
+
|
|
122
|
+
if args.type == "all":
|
|
123
|
+
for dtype in ["posts", "comments", "insights", "user_insights", "templates", "actions"]:
|
|
124
|
+
records, name = get_data(dtype)
|
|
125
|
+
if records:
|
|
126
|
+
do_export(records, name, args.format, output_dir)
|
|
127
|
+
else:
|
|
128
|
+
print(f"[{dtype}] Sem dados.")
|
|
129
|
+
else:
|
|
130
|
+
records, name = get_data(args.type)
|
|
131
|
+
if not records:
|
|
132
|
+
print("Sem dados para exportar.")
|
|
133
|
+
return
|
|
134
|
+
do_export(records, name, args.format, output_dir)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
if __name__ == "__main__":
|
|
138
|
+
main()
|