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,233 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Governança: rate limiting, audit log e confirmações para ações do Instagram.
|
|
3
|
+
|
|
4
|
+
Rate limits são rastreados via SQLite (action_log table).
|
|
5
|
+
Confirmações usam padrão 2-step: retorna JSON com requires_confirmation,
|
|
6
|
+
Claude apresenta ao usuário, e na segunda chamada com --confirm executa.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import uuid
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
|
+
from typing import Any, Dict, Optional
|
|
14
|
+
|
|
15
|
+
from config import (
|
|
16
|
+
ACTION_CATEGORIES,
|
|
17
|
+
RATE_LIMIT_DMS_PER_HOUR,
|
|
18
|
+
RATE_LIMIT_HASHTAGS_PER_WEEK,
|
|
19
|
+
RATE_LIMIT_PUBLISHES_PER_DAY,
|
|
20
|
+
RATE_LIMIT_REQUESTS_PER_HOUR,
|
|
21
|
+
RATE_LIMIT_WARNING_THRESHOLD,
|
|
22
|
+
)
|
|
23
|
+
from db import Database
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class RateLimitExceeded(Exception):
|
|
27
|
+
"""Limite de taxa excedido."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, limit_type: str, current: int, maximum: int, retry_after_seconds: int):
|
|
30
|
+
self.limit_type = limit_type
|
|
31
|
+
self.current = current
|
|
32
|
+
self.maximum = maximum
|
|
33
|
+
self.retry_after_seconds = retry_after_seconds
|
|
34
|
+
super().__init__(
|
|
35
|
+
f"Rate limit '{limit_type}' excedido: {current}/{maximum}. "
|
|
36
|
+
f"Tente novamente em {retry_after_seconds}s."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
40
|
+
return {
|
|
41
|
+
"error": "rate_limit_exceeded",
|
|
42
|
+
"limit_type": self.limit_type,
|
|
43
|
+
"current": self.current,
|
|
44
|
+
"maximum": self.maximum,
|
|
45
|
+
"retry_after_seconds": self.retry_after_seconds,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class GovernanceManager:
|
|
50
|
+
"""Gerencia rate limits, logging e confirmações."""
|
|
51
|
+
|
|
52
|
+
def __init__(self, db: Optional[Database] = None):
|
|
53
|
+
self.db = db or Database()
|
|
54
|
+
self.db.init()
|
|
55
|
+
|
|
56
|
+
# ── Rate Limiting ─────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
def check_rate_limit(self, action: str, account_id: Optional[int] = None) -> Dict[str, Any]:
|
|
59
|
+
"""
|
|
60
|
+
Verifica rate limits antes de uma ação.
|
|
61
|
+
Retorna dict com remaining e warnings.
|
|
62
|
+
Raises RateLimitExceeded se o limite foi atingido.
|
|
63
|
+
"""
|
|
64
|
+
requests_used = self.db.count_requests_last_hour()
|
|
65
|
+
publishes_used = self.db.count_publishes_today()
|
|
66
|
+
|
|
67
|
+
result = {
|
|
68
|
+
"requests": {
|
|
69
|
+
"used": requests_used,
|
|
70
|
+
"limit": RATE_LIMIT_REQUESTS_PER_HOUR,
|
|
71
|
+
"remaining": RATE_LIMIT_REQUESTS_PER_HOUR - requests_used,
|
|
72
|
+
},
|
|
73
|
+
"publishes": {
|
|
74
|
+
"used": publishes_used,
|
|
75
|
+
"limit": RATE_LIMIT_PUBLISHES_PER_DAY,
|
|
76
|
+
"remaining": RATE_LIMIT_PUBLISHES_PER_DAY - publishes_used,
|
|
77
|
+
},
|
|
78
|
+
"warnings": [],
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Verificar limite de requests
|
|
82
|
+
if requests_used >= RATE_LIMIT_REQUESTS_PER_HOUR:
|
|
83
|
+
raise RateLimitExceeded(
|
|
84
|
+
"requests_per_hour", requests_used,
|
|
85
|
+
RATE_LIMIT_REQUESTS_PER_HOUR, 3600,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Verificar limite de publicações
|
|
89
|
+
if action.startswith("publish_") and publishes_used >= RATE_LIMIT_PUBLISHES_PER_DAY:
|
|
90
|
+
raise RateLimitExceeded(
|
|
91
|
+
"publishes_per_day", publishes_used,
|
|
92
|
+
RATE_LIMIT_PUBLISHES_PER_DAY, 86400,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Verificar limite de hashtags
|
|
96
|
+
if action == "search_hashtag" and account_id:
|
|
97
|
+
hashtag_count = self.db.count_hashtag_searches_last_week(account_id)
|
|
98
|
+
result["hashtags"] = {
|
|
99
|
+
"used": hashtag_count,
|
|
100
|
+
"limit": RATE_LIMIT_HASHTAGS_PER_WEEK,
|
|
101
|
+
"remaining": RATE_LIMIT_HASHTAGS_PER_WEEK - hashtag_count,
|
|
102
|
+
}
|
|
103
|
+
if hashtag_count >= RATE_LIMIT_HASHTAGS_PER_WEEK:
|
|
104
|
+
raise RateLimitExceeded(
|
|
105
|
+
"hashtags_per_week", hashtag_count,
|
|
106
|
+
RATE_LIMIT_HASHTAGS_PER_WEEK, 604800,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Warnings em 90% do limite
|
|
110
|
+
if requests_used >= RATE_LIMIT_REQUESTS_PER_HOUR * RATE_LIMIT_WARNING_THRESHOLD:
|
|
111
|
+
result["warnings"].append(
|
|
112
|
+
f"Atenção: {requests_used}/{RATE_LIMIT_REQUESTS_PER_HOUR} requests na última hora"
|
|
113
|
+
)
|
|
114
|
+
if publishes_used >= RATE_LIMIT_PUBLISHES_PER_DAY * RATE_LIMIT_WARNING_THRESHOLD:
|
|
115
|
+
result["warnings"].append(
|
|
116
|
+
f"Atenção: {publishes_used}/{RATE_LIMIT_PUBLISHES_PER_DAY} publicações hoje"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
return result
|
|
120
|
+
|
|
121
|
+
def get_rate_status(self) -> Dict[str, Any]:
|
|
122
|
+
"""Retorna status atual de todos os rate limits."""
|
|
123
|
+
return {
|
|
124
|
+
"requests_per_hour": {
|
|
125
|
+
"used": self.db.count_requests_last_hour(),
|
|
126
|
+
"limit": RATE_LIMIT_REQUESTS_PER_HOUR,
|
|
127
|
+
},
|
|
128
|
+
"publishes_per_day": {
|
|
129
|
+
"used": self.db.count_publishes_today(),
|
|
130
|
+
"limit": RATE_LIMIT_PUBLISHES_PER_DAY,
|
|
131
|
+
},
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
# ── Action Logging ────────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
def log_action(
|
|
137
|
+
self,
|
|
138
|
+
action: str,
|
|
139
|
+
params: Optional[Dict] = None,
|
|
140
|
+
result: Optional[Dict] = None,
|
|
141
|
+
confirmed: bool = True,
|
|
142
|
+
account_id: Optional[int] = None,
|
|
143
|
+
) -> None:
|
|
144
|
+
"""Registra uma ação no audit log."""
|
|
145
|
+
rate_status = self.get_rate_status()
|
|
146
|
+
self.db.log_action({
|
|
147
|
+
"account_id": account_id,
|
|
148
|
+
"action": action,
|
|
149
|
+
"params": json.dumps(params, ensure_ascii=False) if params else None,
|
|
150
|
+
"result": json.dumps(result, ensure_ascii=False) if result else None,
|
|
151
|
+
"confirmed": 1 if confirmed else 0,
|
|
152
|
+
"rate_remaining": json.dumps(rate_status),
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
# ── Confirmation ──────────────────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
def requires_confirmation(self, action: str) -> bool:
|
|
158
|
+
"""Verifica se uma ação requer confirmação do usuário."""
|
|
159
|
+
for category in ("ENGAGE", "PUBLISH", "DELETE", "MESSAGE"):
|
|
160
|
+
if action in ACTION_CATEGORIES.get(category, []):
|
|
161
|
+
return True
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
def get_confirmation_category(self, action: str) -> str:
|
|
165
|
+
"""Retorna a categoria de confirmação de uma ação."""
|
|
166
|
+
for category, actions in ACTION_CATEGORIES.items():
|
|
167
|
+
if action in actions:
|
|
168
|
+
return category
|
|
169
|
+
return "READ"
|
|
170
|
+
|
|
171
|
+
def create_confirmation_request(
|
|
172
|
+
self,
|
|
173
|
+
action: str,
|
|
174
|
+
details: Dict[str, Any],
|
|
175
|
+
account_id: Optional[int] = None,
|
|
176
|
+
) -> Dict[str, Any]:
|
|
177
|
+
"""
|
|
178
|
+
Cria um pedido de confirmação para o Claude apresentar ao usuário.
|
|
179
|
+
Retorna JSON estruturado com action_id para confirmar depois.
|
|
180
|
+
"""
|
|
181
|
+
action_id = str(uuid.uuid4())[:8]
|
|
182
|
+
rate_status = self.get_rate_status()
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
"requires_confirmation": True,
|
|
186
|
+
"action_id": action_id,
|
|
187
|
+
"action": action,
|
|
188
|
+
"category": self.get_confirmation_category(action),
|
|
189
|
+
"details": details,
|
|
190
|
+
"rate_limits": rate_status,
|
|
191
|
+
"message": self._format_confirmation_message(action, details, rate_status),
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
def _format_confirmation_message(
|
|
195
|
+
self, action: str, details: Dict[str, Any], rate_status: Dict[str, Any],
|
|
196
|
+
) -> str:
|
|
197
|
+
"""Formata mensagem de confirmação legível."""
|
|
198
|
+
category = self.get_confirmation_category(action)
|
|
199
|
+
action_names = {
|
|
200
|
+
"publish_photo": "PUBLICAR uma foto",
|
|
201
|
+
"publish_video": "PUBLICAR um vídeo",
|
|
202
|
+
"publish_reel": "PUBLICAR um reel",
|
|
203
|
+
"publish_story": "PUBLICAR um story",
|
|
204
|
+
"publish_carousel": "PUBLICAR um carrossel",
|
|
205
|
+
"schedule_post": "AGENDAR uma publicação",
|
|
206
|
+
"reply_comment": "RESPONDER a um comentário",
|
|
207
|
+
"delete_comment": "DELETAR um comentário",
|
|
208
|
+
"hide_comment": "OCULTAR um comentário",
|
|
209
|
+
"send_dm": "ENVIAR uma mensagem direta",
|
|
210
|
+
}
|
|
211
|
+
action_desc = action_names.get(action, action)
|
|
212
|
+
|
|
213
|
+
lines = [f"[CONFIRMAÇÃO] Prestes a {action_desc}:"]
|
|
214
|
+
for key, value in details.items():
|
|
215
|
+
if value is not None:
|
|
216
|
+
lines.append(f" {key}: {value}")
|
|
217
|
+
|
|
218
|
+
req = rate_status["requests_per_hour"]
|
|
219
|
+
pub = rate_status["publishes_per_day"]
|
|
220
|
+
lines.append(f"\n Rate limits: {req['used']}/{req['limit']} requests/hr, "
|
|
221
|
+
f"{pub['used']}/{pub['limit']} publicações/dia")
|
|
222
|
+
|
|
223
|
+
return "\n".join(lines)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
# ── CLI para verificação ─────────────────────────────────────────────────────
|
|
227
|
+
if __name__ == "__main__":
|
|
228
|
+
gov = GovernanceManager()
|
|
229
|
+
status = gov.get_rate_status()
|
|
230
|
+
print(json.dumps(status, indent=2))
|
|
231
|
+
print("\nÚltimas ações:")
|
|
232
|
+
for a in gov.db.get_recent_actions(10):
|
|
233
|
+
print(f" [{a['created_at']}] {a['action']}")
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pesquisa e tracking de hashtags do Instagram.
|
|
3
|
+
|
|
4
|
+
Uso:
|
|
5
|
+
python scripts/hashtags.py --search "artificialintelligence" --limit 25
|
|
6
|
+
python scripts/hashtags.py --top "tecnologia"
|
|
7
|
+
python scripts/hashtags.py --info "marketing"
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import asyncio
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
18
|
+
|
|
19
|
+
from api_client import InstagramAPI
|
|
20
|
+
from auth import auto_refresh_if_needed
|
|
21
|
+
from db import Database
|
|
22
|
+
from governance import GovernanceManager, RateLimitExceeded
|
|
23
|
+
|
|
24
|
+
db = Database()
|
|
25
|
+
db.init()
|
|
26
|
+
gov = GovernanceManager(db)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def search_hashtag(hashtag: str, limit: int = 25, mode: str = "recent") -> None:
|
|
30
|
+
"""Busca posts com uma hashtag."""
|
|
31
|
+
await auto_refresh_if_needed()
|
|
32
|
+
|
|
33
|
+
account = db.get_active_account()
|
|
34
|
+
if not account:
|
|
35
|
+
print(json.dumps({"error": "Nenhuma conta configurada"}, indent=2))
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
# Verificar rate limit de hashtags
|
|
39
|
+
try:
|
|
40
|
+
gov.check_rate_limit("search_hashtag", account["id"])
|
|
41
|
+
except RateLimitExceeded as e:
|
|
42
|
+
print(json.dumps(e.to_dict(), indent=2))
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
api = InstagramAPI()
|
|
46
|
+
|
|
47
|
+
# Step 1: Buscar ID da hashtag
|
|
48
|
+
search_result = await api.search_hashtag(hashtag)
|
|
49
|
+
hashtag_data = search_result.get("data", [])
|
|
50
|
+
if not hashtag_data:
|
|
51
|
+
print(json.dumps({"error": f"Hashtag '{hashtag}' não encontrada"}, indent=2))
|
|
52
|
+
await api.close()
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
hashtag_id = hashtag_data[0]["id"]
|
|
56
|
+
|
|
57
|
+
# Registrar busca
|
|
58
|
+
db.insert_hashtag_search({
|
|
59
|
+
"account_id": account["id"],
|
|
60
|
+
"hashtag": hashtag,
|
|
61
|
+
"ig_hashtag_id": hashtag_id,
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
# Step 2: Buscar posts
|
|
65
|
+
if mode == "top":
|
|
66
|
+
result = await api.get_hashtag_top_media(hashtag_id, limit=limit)
|
|
67
|
+
else:
|
|
68
|
+
result = await api.get_hashtag_recent_media(hashtag_id, limit=limit)
|
|
69
|
+
|
|
70
|
+
await api.close()
|
|
71
|
+
|
|
72
|
+
# Contagem de buscas na semana
|
|
73
|
+
weekly_count = db.count_hashtag_searches_last_week(account["id"])
|
|
74
|
+
|
|
75
|
+
output = {
|
|
76
|
+
"hashtag": hashtag,
|
|
77
|
+
"hashtag_id": hashtag_id,
|
|
78
|
+
"mode": mode,
|
|
79
|
+
"results": result.get("data", []),
|
|
80
|
+
"total": len(result.get("data", [])),
|
|
81
|
+
"hashtag_searches_this_week": weekly_count,
|
|
82
|
+
"hashtag_searches_limit": 30,
|
|
83
|
+
}
|
|
84
|
+
print(json.dumps(output, indent=2, ensure_ascii=False))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
async def hashtag_info(hashtag: str) -> None:
|
|
88
|
+
"""Info de uma hashtag (apenas o ID — media_count requer permissões especiais)."""
|
|
89
|
+
await auto_refresh_if_needed()
|
|
90
|
+
api = InstagramAPI()
|
|
91
|
+
result = await api.search_hashtag(hashtag)
|
|
92
|
+
await api.close()
|
|
93
|
+
print(json.dumps({"hashtag": hashtag, "data": result.get("data", [])}, indent=2, ensure_ascii=False))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def main():
|
|
97
|
+
parser = argparse.ArgumentParser(description="Hashtags do Instagram")
|
|
98
|
+
group = parser.add_mutually_exclusive_group(required=True)
|
|
99
|
+
group.add_argument("--search", metavar="TAG", help="Buscar posts recentes com hashtag")
|
|
100
|
+
group.add_argument("--top", metavar="TAG", help="Top posts de uma hashtag")
|
|
101
|
+
group.add_argument("--info", metavar="TAG", help="Info da hashtag")
|
|
102
|
+
parser.add_argument("--limit", type=int, default=25, help="Limite de resultados")
|
|
103
|
+
args = parser.parse_args()
|
|
104
|
+
|
|
105
|
+
if args.search:
|
|
106
|
+
asyncio.run(search_hashtag(args.search, args.limit, mode="recent"))
|
|
107
|
+
elif args.top:
|
|
108
|
+
asyncio.run(search_hashtag(args.top, args.limit, mode="top"))
|
|
109
|
+
elif args.info:
|
|
110
|
+
asyncio.run(hashtag_info(args.info))
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
if __name__ == "__main__":
|
|
114
|
+
main()
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Analytics e insights do Instagram.
|
|
3
|
+
|
|
4
|
+
Uso:
|
|
5
|
+
python scripts/insights.py --media --media-id 12345
|
|
6
|
+
python scripts/insights.py --user --period day --since 7
|
|
7
|
+
python scripts/insights.py --fetch-all --limit 20
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import asyncio
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
from datetime import datetime, timedelta, timezone
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
19
|
+
|
|
20
|
+
from api_client import InstagramAPI
|
|
21
|
+
from auth import auto_refresh_if_needed
|
|
22
|
+
from db import Database
|
|
23
|
+
|
|
24
|
+
db = Database()
|
|
25
|
+
db.init()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def media_insights(media_id: str, metrics: list = None) -> None:
|
|
29
|
+
"""Busca insights de um post específico."""
|
|
30
|
+
await auto_refresh_if_needed()
|
|
31
|
+
api = InstagramAPI()
|
|
32
|
+
try:
|
|
33
|
+
result = await api.get_media_insights(media_id, metrics=metrics)
|
|
34
|
+
except Exception as e:
|
|
35
|
+
print(json.dumps({"error": str(e), "media_id": media_id}, indent=2))
|
|
36
|
+
await api.close()
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
# Salvar no banco
|
|
40
|
+
account = db.get_active_account()
|
|
41
|
+
if account:
|
|
42
|
+
raw = json.dumps(result, ensure_ascii=False)
|
|
43
|
+
for item in result.get("data", []):
|
|
44
|
+
values = item.get("values", [{}])
|
|
45
|
+
value = values[0].get("value", 0) if values else 0
|
|
46
|
+
db.insert_insights([{
|
|
47
|
+
"account_id": account["id"],
|
|
48
|
+
"ig_media_id": media_id,
|
|
49
|
+
"metric_name": item.get("name", ""),
|
|
50
|
+
"metric_value": float(value) if isinstance(value, (int, float)) else 0,
|
|
51
|
+
"period": item.get("period", ""),
|
|
52
|
+
"raw_json": raw,
|
|
53
|
+
}])
|
|
54
|
+
|
|
55
|
+
await api.close()
|
|
56
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
async def user_insights(period: str = "day", since_days: int = 7, metrics: list = None) -> None:
|
|
60
|
+
"""Busca insights da conta."""
|
|
61
|
+
await auto_refresh_if_needed()
|
|
62
|
+
api = InstagramAPI()
|
|
63
|
+
|
|
64
|
+
now = datetime.now(timezone.utc)
|
|
65
|
+
since = (now - timedelta(days=since_days)).strftime("%Y-%m-%d")
|
|
66
|
+
until = now.strftime("%Y-%m-%d")
|
|
67
|
+
|
|
68
|
+
result = await api.get_user_insights(
|
|
69
|
+
period=period,
|
|
70
|
+
metrics=metrics,
|
|
71
|
+
since=since,
|
|
72
|
+
until=until,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Salvar no banco
|
|
76
|
+
account = db.get_active_account()
|
|
77
|
+
if account:
|
|
78
|
+
for item in result.get("data", []):
|
|
79
|
+
for value_entry in item.get("values", []):
|
|
80
|
+
db.insert_user_insights([{
|
|
81
|
+
"account_id": account["id"],
|
|
82
|
+
"metric_name": item.get("name", ""),
|
|
83
|
+
"metric_value": float(value_entry.get("value", 0)),
|
|
84
|
+
"period": period,
|
|
85
|
+
"end_time": value_entry.get("end_time", ""),
|
|
86
|
+
}])
|
|
87
|
+
|
|
88
|
+
await api.close()
|
|
89
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
async def fetch_all_insights(limit: int = 20) -> None:
|
|
93
|
+
"""Busca insights de todos os posts recentes."""
|
|
94
|
+
await auto_refresh_if_needed()
|
|
95
|
+
api = InstagramAPI()
|
|
96
|
+
|
|
97
|
+
media_result = await api.get_user_media(limit=limit)
|
|
98
|
+
media_list = media_result.get("data", [])
|
|
99
|
+
|
|
100
|
+
results = []
|
|
101
|
+
for media in media_list:
|
|
102
|
+
media_id = media["id"]
|
|
103
|
+
media_type = media.get("media_type", "IMAGE")
|
|
104
|
+
try:
|
|
105
|
+
# Métricas variam por tipo
|
|
106
|
+
metrics = ["impressions", "reach", "engagement", "saved"]
|
|
107
|
+
if media_type == "VIDEO":
|
|
108
|
+
metrics.append("video_views")
|
|
109
|
+
if media_type == "REELS" or "reel" in media.get("permalink", "").lower():
|
|
110
|
+
metrics = ["impressions", "reach", "likes", "comments", "saves", "shares", "plays"]
|
|
111
|
+
|
|
112
|
+
insights = await api.get_media_insights(media_id, metrics=metrics)
|
|
113
|
+
|
|
114
|
+
# Salvar
|
|
115
|
+
account = db.get_active_account()
|
|
116
|
+
if account:
|
|
117
|
+
raw = json.dumps(insights, ensure_ascii=False)
|
|
118
|
+
for item in insights.get("data", []):
|
|
119
|
+
values = item.get("values", [{}])
|
|
120
|
+
value = values[0].get("value", 0) if values else 0
|
|
121
|
+
db.insert_insights([{
|
|
122
|
+
"account_id": account["id"],
|
|
123
|
+
"ig_media_id": media_id,
|
|
124
|
+
"metric_name": item.get("name", ""),
|
|
125
|
+
"metric_value": float(value) if isinstance(value, (int, float)) else 0,
|
|
126
|
+
"period": item.get("period", ""),
|
|
127
|
+
"raw_json": raw,
|
|
128
|
+
}])
|
|
129
|
+
|
|
130
|
+
results.append({
|
|
131
|
+
"media_id": media_id,
|
|
132
|
+
"type": media_type,
|
|
133
|
+
"caption": (media.get("caption", "") or "")[:50],
|
|
134
|
+
"metrics": {
|
|
135
|
+
d["name"]: d["values"][0]["value"] if d.get("values") else 0
|
|
136
|
+
for d in insights.get("data", [])
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
except Exception as e:
|
|
140
|
+
results.append({"media_id": media_id, "error": str(e)})
|
|
141
|
+
|
|
142
|
+
await api.close()
|
|
143
|
+
print(json.dumps({"fetched": len(results), "insights": results}, indent=2, ensure_ascii=False))
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def main():
|
|
147
|
+
parser = argparse.ArgumentParser(description="Insights do Instagram")
|
|
148
|
+
group = parser.add_mutually_exclusive_group(required=True)
|
|
149
|
+
group.add_argument("--media", action="store_true", help="Insights de um post")
|
|
150
|
+
group.add_argument("--user", action="store_true", help="Insights da conta")
|
|
151
|
+
group.add_argument("--fetch-all", action="store_true", help="Buscar insights de todos os posts recentes")
|
|
152
|
+
parser.add_argument("--media-id", help="ID da mídia")
|
|
153
|
+
parser.add_argument("--period", default="day", choices=["day", "week", "days_28", "month", "lifetime"])
|
|
154
|
+
parser.add_argument("--since", type=int, default=7, help="Dias atrás (default: 7)")
|
|
155
|
+
parser.add_argument("--metrics", nargs="+", help="Métricas específicas")
|
|
156
|
+
parser.add_argument("--limit", type=int, default=20, help="Limite de posts para --fetch-all")
|
|
157
|
+
args = parser.parse_args()
|
|
158
|
+
|
|
159
|
+
if args.media:
|
|
160
|
+
if not args.media_id:
|
|
161
|
+
parser.error("--media-id é obrigatório com --media")
|
|
162
|
+
asyncio.run(media_insights(args.media_id, args.metrics))
|
|
163
|
+
elif args.user:
|
|
164
|
+
asyncio.run(user_insights(args.period, args.since, args.metrics))
|
|
165
|
+
elif args.fetch_all:
|
|
166
|
+
asyncio.run(fetch_all_insights(args.limit))
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
if __name__ == "__main__":
|
|
170
|
+
main()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Listagem e detalhes de mídia do Instagram.
|
|
3
|
+
|
|
4
|
+
Uso:
|
|
5
|
+
python scripts/media.py --list [--limit 10]
|
|
6
|
+
python scripts/media.py --details --media-id 12345
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import asyncio
|
|
12
|
+
import json
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
17
|
+
|
|
18
|
+
from api_client import InstagramAPI
|
|
19
|
+
from auth import auto_refresh_if_needed
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def list_media(limit: int = 25, after: str = None) -> None:
|
|
23
|
+
"""Lista mídia do usuário."""
|
|
24
|
+
await auto_refresh_if_needed()
|
|
25
|
+
api = InstagramAPI()
|
|
26
|
+
result = await api.get_user_media(limit=limit, after=after)
|
|
27
|
+
await api.close()
|
|
28
|
+
|
|
29
|
+
data = result.get("data", [])
|
|
30
|
+
print(json.dumps({
|
|
31
|
+
"total": len(data),
|
|
32
|
+
"media": data,
|
|
33
|
+
"paging": result.get("paging", {}),
|
|
34
|
+
}, indent=2, ensure_ascii=False))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def media_details(media_id: str) -> None:
|
|
38
|
+
"""Detalhes de uma mídia específica."""
|
|
39
|
+
await auto_refresh_if_needed()
|
|
40
|
+
api = InstagramAPI()
|
|
41
|
+
result = await api.get_media_details(media_id)
|
|
42
|
+
await api.close()
|
|
43
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def main():
|
|
47
|
+
parser = argparse.ArgumentParser(description="Mídia do Instagram")
|
|
48
|
+
group = parser.add_mutually_exclusive_group(required=True)
|
|
49
|
+
group.add_argument("--list", action="store_true", help="Listar mídia")
|
|
50
|
+
group.add_argument("--details", action="store_true", help="Detalhes de uma mídia")
|
|
51
|
+
parser.add_argument("--limit", type=int, default=25, help="Limite de resultados")
|
|
52
|
+
parser.add_argument("--media-id", help="ID da mídia")
|
|
53
|
+
parser.add_argument("--after", help="Cursor de paginação")
|
|
54
|
+
args = parser.parse_args()
|
|
55
|
+
|
|
56
|
+
if args.list:
|
|
57
|
+
asyncio.run(list_media(args.limit, args.after))
|
|
58
|
+
elif args.details:
|
|
59
|
+
if not args.media_id:
|
|
60
|
+
parser.error("--media-id é obrigatório com --details")
|
|
61
|
+
asyncio.run(media_details(args.media_id))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
main()
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mensagens diretas do Instagram (DMs).
|
|
3
|
+
|
|
4
|
+
Uso:
|
|
5
|
+
python scripts/messages.py --send --user-id 12345 --text "Olá!"
|
|
6
|
+
python scripts/messages.py --conversations
|
|
7
|
+
python scripts/messages.py --thread --conversation-id 12345
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import asyncio
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
18
|
+
|
|
19
|
+
from api_client import InstagramAPI
|
|
20
|
+
from auth import auto_refresh_if_needed
|
|
21
|
+
from db import Database
|
|
22
|
+
from governance import GovernanceManager
|
|
23
|
+
|
|
24
|
+
db = Database()
|
|
25
|
+
db.init()
|
|
26
|
+
gov = GovernanceManager(db)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def send_message(user_id: str, text: str) -> None:
|
|
30
|
+
"""Envia DM para um usuário."""
|
|
31
|
+
await auto_refresh_if_needed()
|
|
32
|
+
|
|
33
|
+
if gov.requires_confirmation("send_dm"):
|
|
34
|
+
result = gov.create_confirmation_request(
|
|
35
|
+
"send_dm",
|
|
36
|
+
{"recipient_id": user_id, "text": text},
|
|
37
|
+
)
|
|
38
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
api = InstagramAPI()
|
|
42
|
+
result = await api.send_message(user_id, text)
|
|
43
|
+
await api.close()
|
|
44
|
+
|
|
45
|
+
account = db.get_active_account()
|
|
46
|
+
gov.log_action(
|
|
47
|
+
"send_dm",
|
|
48
|
+
params={"recipient_id": user_id, "text": text},
|
|
49
|
+
result=result,
|
|
50
|
+
account_id=account["id"] if account else None,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
print(json.dumps({"status": "sent", "result": result}, indent=2, ensure_ascii=False))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
async def list_conversations(limit: int = 20) -> None:
|
|
57
|
+
"""Lista conversas recentes."""
|
|
58
|
+
await auto_refresh_if_needed()
|
|
59
|
+
api = InstagramAPI()
|
|
60
|
+
result = await api.get_conversations(limit=limit)
|
|
61
|
+
await api.close()
|
|
62
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
async def show_thread(conversation_id: str) -> None:
|
|
66
|
+
"""Mostra mensagens de uma conversa."""
|
|
67
|
+
await auto_refresh_if_needed()
|
|
68
|
+
api = InstagramAPI()
|
|
69
|
+
result = await api.get(
|
|
70
|
+
f"{conversation_id}/messages",
|
|
71
|
+
params={"fields": "id,message,from,created_time"},
|
|
72
|
+
action="get_thread",
|
|
73
|
+
)
|
|
74
|
+
await api.close()
|
|
75
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def main():
|
|
79
|
+
parser = argparse.ArgumentParser(description="DMs do Instagram")
|
|
80
|
+
group = parser.add_mutually_exclusive_group(required=True)
|
|
81
|
+
group.add_argument("--send", action="store_true", help="Enviar DM")
|
|
82
|
+
group.add_argument("--conversations", action="store_true", help="Listar conversas")
|
|
83
|
+
group.add_argument("--thread", action="store_true", help="Ver mensagens de uma conversa")
|
|
84
|
+
parser.add_argument("--user-id", help="ID do destinatário")
|
|
85
|
+
parser.add_argument("--text", help="Texto da mensagem")
|
|
86
|
+
parser.add_argument("--conversation-id", help="ID da conversa")
|
|
87
|
+
parser.add_argument("--limit", type=int, default=20, help="Limite")
|
|
88
|
+
args = parser.parse_args()
|
|
89
|
+
|
|
90
|
+
if args.send:
|
|
91
|
+
if not args.user_id or not args.text:
|
|
92
|
+
parser.error("--user-id e --text são obrigatórios com --send")
|
|
93
|
+
asyncio.run(send_message(args.user_id, args.text))
|
|
94
|
+
elif args.conversations:
|
|
95
|
+
asyncio.run(list_conversations(args.limit))
|
|
96
|
+
elif args.thread:
|
|
97
|
+
if not args.conversation_id:
|
|
98
|
+
parser.error("--conversation-id é obrigatório com --thread")
|
|
99
|
+
asyncio.run(show_thread(args.conversation_id))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
if __name__ == "__main__":
|
|
103
|
+
main()
|