-
-
Notifications
You must be signed in to change notification settings - Fork 1
Claude/configurable news reader g1 gdx #57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4a4cb3a
1a13d20
6c982ba
3102127
0ce0082
7a8b414
f065484
f256beb
d438f50
55b9641
5b41d39
2c2164c
ad89e01
2a10380
2333504
ecc9937
49f7f5e
dab78fd
655c2c6
95bda56
2ad31a0
9c4c51e
9fe95ff
618507d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| blank_issues_enabled: true | ||
| contact_links: | ||
| - name: AI-Pulse Reader | ||
| url: https://thephoenixagency.github.io/AI-Pulse/app.html | ||
| about: Accedez au reader en ligne |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,65 @@ | ||||||
| name: Proposer une nouvelle source / Suggest a new source | ||||||
| description: Proposez une source RSS a ajouter a AI-Pulse | ||||||
| title: "[SOURCE] " | ||||||
| labels: ["new-source"] | ||||||
| body: | ||||||
| - type: input | ||||||
| id: source_name | ||||||
| attributes: | ||||||
| label: Nom de la source / Source Name | ||||||
| placeholder: "Ex: Korben, TechCrunch..." | ||||||
| validations: | ||||||
| required: true | ||||||
| - type: input | ||||||
| id: source_url | ||||||
| attributes: | ||||||
| label: URL du site / Website URL | ||||||
| placeholder: "https://example.com" | ||||||
| validations: | ||||||
| required: true | ||||||
| - type: input | ||||||
| id: rss_url | ||||||
| attributes: | ||||||
| label: URL du flux RSS / RSS Feed URL | ||||||
| placeholder: "https://example.com/feed/" | ||||||
| validations: | ||||||
| required: true | ||||||
| - type: dropdown | ||||||
| id: category | ||||||
| attributes: | ||||||
| label: Categorie / Category | ||||||
| options: | ||||||
| - AI / IA | ||||||
| - Cybersecurity / Cybersecurite | ||||||
|
||||||
| - Cybersecurity / Cybersecurite | |
| - Cybersecurity / Cybersécurité |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,48 @@ | ||||||
| name: S'abonner a AI-Pulse / Subscribe to AI-Pulse | ||||||
| description: Recevez votre digest personnalise par email | ||||||
| title: "[SUBSCRIBE] " | ||||||
| labels: ["subscription"] | ||||||
| body: | ||||||
| - type: input | ||||||
| id: email | ||||||
| attributes: | ||||||
| label: Votre email / Your email | ||||||
| placeholder: "you@example.com" | ||||||
| validations: | ||||||
| required: true | ||||||
| - type: checkboxes | ||||||
| id: categories | ||||||
| attributes: | ||||||
| label: Categories souhaitees / Desired categories | ||||||
|
||||||
| label: Categories souhaitees / Desired categories | |
| label: Categories souhaitées / Desired categories |
Copilot
AI
Feb 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The French word "preferee" is missing accents. It should be "préférée".
| label: Langue preferee / Preferred language | |
| label: Langue préférée / Preferred language |
Copilot
AI
Feb 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The French word "Frequence" is missing an accent. It should be "Fréquence".
| label: Frequence / Frequency | |
| label: Fréquence / Frequency |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,122 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| name: Auto-add approved source | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||||||||||||||||||||
| issues: | ||||||||||||||||||||||||||||||||||||||||||||
| types: [labeled] | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| permissions: | ||||||||||||||||||||||||||||||||||||||||||||
| contents: write | ||||||||||||||||||||||||||||||||||||||||||||
| issues: write | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||||||||||||||||
| add-source: | ||||||||||||||||||||||||||||||||||||||||||||
| if: github.event.label.name == 'source-approved' | ||||||||||||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||||||||||||
| - uses: actions/checkout@v4 | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| - name: Parse issue and add source | ||||||||||||||||||||||||||||||||||||||||||||
| uses: actions/github-script@v7 | ||||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||||
| script: | | ||||||||||||||||||||||||||||||||||||||||||||
| const fs = require('fs'); | ||||||||||||||||||||||||||||||||||||||||||||
| const issue = context.payload.issue; | ||||||||||||||||||||||||||||||||||||||||||||
| const body = issue.body; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Parse form fields from issue body | ||||||||||||||||||||||||||||||||||||||||||||
| function getField(label) { | ||||||||||||||||||||||||||||||||||||||||||||
| const regex = new RegExp(`### ${label}\\s*\\n\\s*(.+)`, 'i'); | ||||||||||||||||||||||||||||||||||||||||||||
| const match = body.match(regex); | ||||||||||||||||||||||||||||||||||||||||||||
| return match ? match[1].trim() : ''; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const sourceName = getField('Nom de la source / Source Name'); | ||||||||||||||||||||||||||||||||||||||||||||
| const sourceUrl = getField('URL du site / Website URL'); | ||||||||||||||||||||||||||||||||||||||||||||
| const rssUrl = getField('URL du flux RSS / RSS Feed URL'); | ||||||||||||||||||||||||||||||||||||||||||||
| const categoryRaw = getField('Categorie / Category'); | ||||||||||||||||||||||||||||||||||||||||||||
| const languageRaw = getField('Langue / Language'); | ||||||||||||||||||||||||||||||||||||||||||||
| const tags = getField('Tags / Mots-cles'); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| if (!sourceName || !rssUrl) { | ||||||||||||||||||||||||||||||||||||||||||||
| console.log('Missing required fields'); | ||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Map category | ||||||||||||||||||||||||||||||||||||||||||||
| const catMap = { | ||||||||||||||||||||||||||||||||||||||||||||
| 'AI / IA': 'ai', | ||||||||||||||||||||||||||||||||||||||||||||
| 'Cybersecurity / Cybersecurite': 'cybersecurity', | ||||||||||||||||||||||||||||||||||||||||||||
| 'IoT': 'iot', | ||||||||||||||||||||||||||||||||||||||||||||
| 'Windows': 'windows', | ||||||||||||||||||||||||||||||||||||||||||||
| 'Mac / Apple': 'mac', | ||||||||||||||||||||||||||||||||||||||||||||
| 'Linux': 'linux', | ||||||||||||||||||||||||||||||||||||||||||||
| 'Tech Generale': 'tech', | ||||||||||||||||||||||||||||||||||||||||||||
| 'Entrepreneuriat / Entrepreneurship': 'entrepreneurship', | ||||||||||||||||||||||||||||||||||||||||||||
| 'Bourse & Finance': 'finance', | ||||||||||||||||||||||||||||||||||||||||||||
| 'Crypto & Blockchain': 'crypto', | ||||||||||||||||||||||||||||||||||||||||||||
| 'Open Source & GitHub': 'opensource', | ||||||||||||||||||||||||||||||||||||||||||||
| 'Produits & Innovation': 'products' | ||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||
| const category = catMap[categoryRaw] || 'tech'; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Map language | ||||||||||||||||||||||||||||||||||||||||||||
| const lang = languageRaw.includes('Francais') ? 'fr' : 'en'; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Parse tags | ||||||||||||||||||||||||||||||||||||||||||||
| const tagList = tags ? tags.split(',').map(t => t.trim()).filter(t => t) : [category]; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Read and update config.json | ||||||||||||||||||||||||||||||||||||||||||||
| const config = JSON.parse(fs.readFileSync('config.json', 'utf-8')); | ||||||||||||||||||||||||||||||||||||||||||||
| if (!config.categories[category]) { | ||||||||||||||||||||||||||||||||||||||||||||
| console.log(`Category ${category} not found`); | ||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Check for duplicates | ||||||||||||||||||||||||||||||||||||||||||||
| const exists = config.categories[category].feeds.some(f => f.url === rssUrl); | ||||||||||||||||||||||||||||||||||||||||||||
| if (exists) { | ||||||||||||||||||||||||||||||||||||||||||||
| console.log('Source already exists'); | ||||||||||||||||||||||||||||||||||||||||||||
| await github.rest.issues.createComment({ | ||||||||||||||||||||||||||||||||||||||||||||
| owner: context.repo.owner, | ||||||||||||||||||||||||||||||||||||||||||||
| repo: context.repo.repo, | ||||||||||||||||||||||||||||||||||||||||||||
| issue_number: issue.number, | ||||||||||||||||||||||||||||||||||||||||||||
| body: `This source (${rssUrl}) already exists in the ${category} category.` | ||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Add new feed | ||||||||||||||||||||||||||||||||||||||||||||
| config.categories[category].feeds.push({ | ||||||||||||||||||||||||||||||||||||||||||||
| name: sourceName, | ||||||||||||||||||||||||||||||||||||||||||||
| url: rssUrl, | ||||||||||||||||||||||||||||||||||||||||||||
| tags: tagList, | ||||||||||||||||||||||||||||||||||||||||||||
| lang: lang | ||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+88
to
+94
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| fs.writeFileSync('config.json', JSON.stringify(config, null, 2)); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Comment on issue | ||||||||||||||||||||||||||||||||||||||||||||
| await github.rest.issues.createComment({ | ||||||||||||||||||||||||||||||||||||||||||||
| owner: context.repo.owner, | ||||||||||||||||||||||||||||||||||||||||||||
| repo: context.repo.repo, | ||||||||||||||||||||||||||||||||||||||||||||
| issue_number: issue.number, | ||||||||||||||||||||||||||||||||||||||||||||
| body: `Source "${sourceName}" added to category "${category}" in config.json. It will be active at the next aggregation cycle.` | ||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Close issue | ||||||||||||||||||||||||||||||||||||||||||||
| await github.rest.issues.update({ | ||||||||||||||||||||||||||||||||||||||||||||
| owner: context.repo.owner, | ||||||||||||||||||||||||||||||||||||||||||||
| repo: context.repo.repo, | ||||||||||||||||||||||||||||||||||||||||||||
| issue_number: issue.number, | ||||||||||||||||||||||||||||||||||||||||||||
| state: 'closed' | ||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| - name: Commit changes | ||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||
| git config --global user.name 'PhoenixProject-AutoSync' | ||||||||||||||||||||||||||||||||||||||||||||
| git config --global user.email '${{ secrets.GIT_AUTHOR_EMAIL }}' | ||||||||||||||||||||||||||||||||||||||||||||
| git add config.json | ||||||||||||||||||||||||||||||||||||||||||||
| if ! git diff --cached --exit-code; then | ||||||||||||||||||||||||||||||||||||||||||||
| git commit -m "Add new source from issue #${{ github.event.issue.number }}" | ||||||||||||||||||||||||||||||||||||||||||||
| git push | ||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+114
to
+122
|
||||||||||||||||||||||||||||||||||||||||||||
| - name: Commit changes | |
| run: | | |
| git config --global user.name 'PhoenixProject-AutoSync' | |
| git config --global user.email '${{ secrets.GIT_AUTHOR_EMAIL }}' | |
| git add config.json | |
| if ! git diff --cached --exit-code; then | |
| git commit -m "Add new source from issue #${{ github.event.issue.number }}" | |
| git push | |
| fi | |
| - name: Create pull request for config update | |
| uses: peter-evans/create-pull-request@v6 | |
| with: | |
| commit-message: "Add new source from issue #${{ github.event.issue.number }}" | |
| title: "Add new source from issue #${{ github.event.issue.number }}" | |
| body: | | |
| This pull request was automatically generated from issue #${{ github.event.issue.number }} after it was labeled 'source-approved'. | |
| Please review the changes to config.json before merging. | |
| branch: "auto/add-source-${{ github.event.issue.number }}" | |
| add-paths: | | |
| config.json |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,122 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: Manage subscriber | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| issues: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| types: [labeled] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| permissions: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contents: write | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| issues: write | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| add-subscriber: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: github.event.label.name == 'subscription' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - uses: actions/checkout@v4 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Parse and add subscriber | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uses: actions/github-script@v7 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| script: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const fs = require('fs'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const issue = context.payload.issue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const body = issue.body; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function getField(label) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const regex = new RegExp(`### ${label}\\s*\\n\\s*(.+)`, 'i'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const match = body.match(regex); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return match ? match[1].trim() : ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function getCheckboxes(label) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const regex = new RegExp(`### ${label}\\s*\\n([\\s\\S]*?)(?=###|$)`, 'i'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const match = body.match(regex); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!match) return []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [...match[1].matchAll(/- \[X\] (.+)/gi)].map(m => m[1].trim()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const email = getField('Votre email / Your email'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!email || !email.includes('@')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log('Invalid email'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+39
to
+44
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const email = getField('Votre email / Your email'); | |
| if (!email || !email.includes('@')) { | |
| console.log('Invalid email'); | |
| return; | |
| } | |
| function isValidEmail(email) { | |
| const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | |
| return ( | |
| typeof email === 'string' && | |
| email.length > 0 && | |
| email.length <= 254 && | |
| emailRegex.test(email) | |
| ); | |
| } | |
| // Basic abuse heuristic: ignore issues that appear to contain many email-like strings. | |
| const atCount = (body.match(/@/g) || []).length; | |
| if (atCount > 3) { | |
| console.log('Suspicious issue body (too many email-like values); skipping subscription.'); | |
| return; | |
| } | |
| const rawEmail = getField('Votre email / Your email'); | |
| // In case multiple emails or extra text are provided, only keep the first token. | |
| const email = rawEmail.split(/[,\s]+/).filter(Boolean)[0] || ''; | |
| if (!isValidEmail(email)) { | |
| console.log('Invalid email:', rawEmail); | |
| return; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The French word "Categorie" is missing an accent. It should be "Catégorie".