diff --git a/.github/workflows/issue_bot.yml b/.github/workflows/issue_bot.yml new file mode 100644 index 000000000..7b36fd4f4 --- /dev/null +++ b/.github/workflows/issue_bot.yml @@ -0,0 +1,38 @@ +name: Issue Bot + +on: + issues: + types: [opened, edited] + +permissions: + issues: write + contents: read + +jobs: + comment: + runs-on: ubuntu-latest + if: | + contains(github.event.issue.labels.*.name, 'new device support') || + contains(github.event.issue.labels.*.name, 'external converter') || + startsWith(github.event.issue.title, '[New device support]') || + startsWith(github.event.issue.title, '[External Converter]') + steps: + - uses: actions/checkout@v5 + with: + sparse-checkout: | + scripts + path: z2m + + - uses: actions/checkout@v5 + with: + repository: Koenkk/zigbee-herdsman-converters + ref: master + fetch-depth: 1 + path: zhc + + - name: Comment on new device support/external converter issue + uses: actions/github-script@v8 + with: + script: | + const {newDeviceSupport} = await import("${{ github.workspace }}/z2m/scripts/issueBot.mjs") + await newDeviceSupport(github, core, context, "${{ github.workspace }}/zhc") diff --git a/.github/workflows/new_device_external_converter_bot.yml b/.github/workflows/new_device_external_converter_bot.yml deleted file mode 100644 index 2f01b230e..000000000 --- a/.github/workflows/new_device_external_converter_bot.yml +++ /dev/null @@ -1,148 +0,0 @@ -name: New Device Support/External Converter Issue Bot - -on: - issues: - types: [opened, edited] - -permissions: - issues: write - contents: read - -jobs: - comment: - runs-on: ubuntu-latest - steps: - - name: Comment on new device support/external converter issue - uses: actions/github-script@v8 - with: - script: | - const issue = context.payload.issue; - const newDeviceSupport = issue.title && issue.title.startsWith("[New device support]"); - const externalConverter = issue.title && issue.title.startsWith("[External Converter]"); - - // Only proceed for new device support or external converter issues - if (!newDeviceSupport && !externalConverter) { - return; - } - - // Hide previous bot comments - const comments = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue.number, - }); - - for (const comment of comments.data) { - if (comment.user.type === 'Bot' && comment.user.login === 'github-actions[bot]') { - await github.graphql(` - mutation { - minimizeComment(input: {subjectId: "${comment.node_id}", classifier: OUTDATED}) { - clientMutationId - } - } - `); - } - } - - // Checkout the repo and grep for the manufacturer name - const execSync = require('child_process').execSync; - execSync(`git clone https://github.com/Koenkk/zigbee-herdsman-converters.git zhc`, {stdio: 'ignore'}); - - // Check if Tuya manufacturer name is already supported. - const tuyaManufacturerNameRe = /['"](_T\w+_(\w+))['"]/g; - const tuyaManufacturerNames = Array.from(issue.body.matchAll(tuyaManufacturerNameRe), m => [m[1], m[2]]); - if (tuyaManufacturerNames.length > 0) { - for (const [fullName, partialName] of tuyaManufacturerNames) { - const fullMatch = (() => { try { return execSync(`grep -r --include="*.ts" "${fullName}" zhc`, {encoding: 'utf8'}); } catch { return undefined; } })(); - if (fullMatch) { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue.number, - body: [ - `👋 Hi there! The Tuya device with manufacturer name \`${fullName}\` is already supported in the latest dev branch.`, - "See this [guide](https://www.zigbee2mqtt.io/advanced/more/switch-to-dev-branch.html) on how to update, after updating you can remove your external converter.", - "", - "In case you created the external converter with the goal to extend or fix an issue with the out-of-the-box support, please submit a pull request.", - "For instructions on how to create a pull request see the [docs](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html#_4-create-a-pull-request).", - "If you need help with the process, feel free to ask here and we'll be happy to assist." - ].join('\n') - }); - await github.rest.issues.update({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue.number, - state: "closed", - }); - return; - } - - const partialMatch = (() => { try { return execSync(`grep -r --include="*.ts" "${partialName}" zhc`, {encoding: 'utf8'}); } catch { return undefined; } })(); - if (partialMatch) { - const candidates = Array.from(partialMatch.matchAll(tuyaManufacturerNameRe), m => m[1]); - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue.number, - body: [ - `👋 Hi there! A similar Tuya device of which the manufacturer name also ends with \`_${partialName}\` is already supported.`, - "This means the device can probably be easily be supported by re-using the existing converter.", - "", - "I found the following matches: " + candidates.map((c) => `\`${c}\``).join(', '), - `Try to stop Z2M, change all occurrences of \`${fullName}\` in the \`data/database.db\` to one of the matches above and start Z2M.`, - "Let us know if it works so we can support this device out-of-the-box!", - ].join('\n') - }); - return; - } - } - } else { - // Check if zigbee model is already supported. - const zigbeeModelRe = /zigbeeModel: \[['"](.+)['"]\]/g; - const zigbeeModels = Array.from(issue.body.matchAll(zigbeeModelRe), m => m[1]); - if (zigbeeModels.length > 0) { - for (const zigbeeModel of zigbeeModels) { - const fullMatch = (() => { try { return execSync(`grep -r --include="*.ts" '"${zigbeeModel}"' zhc`, {encoding: 'utf8'}); } catch { return undefined; } })(); - if (fullMatch) { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue.number, - body: [ - `👋 Hi there! The device with zigbee model \`${zigbeeModel}\` is already supported in the latest dev branch.`, - "See this [guide](https://www.zigbee2mqtt.io/advanced/more/switch-to-dev-branch.html) on how to update, after updating you can remove your external converter.", - "", - "In case you created the external converter with the goal to extend or fix an issue with the out-of-the-box support, please submit a pull request.", - "For instructions on how to create a pull request see the [docs](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html#_4-create-a-pull-request).", - "If you need help with the process, feel free to ask here and we'll be happy to assist." - ].join('\n') - }); - await github.rest.issues.update({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue.number, - state: "closed", - }); - return; - } - } - } - } - - // Create a request to pull request comment - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue.number, - body: [ - "🙏 Thank you for creating this issue and sharing your external converter!", - "", - "In case all features work, please submit a pull request on this repository so the device can be supported out-of-the-box with the next release.", - "For instructions on how to create a pull request see the [docs](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html#_4-create-a-pull-request).", - "", - "If **NOT** all features work, please follow the [How To Support new devices](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html)", - tuyaManufacturerNames.length > 0 ? "Since this is a Tuya also consider [How To Support new Tuya devices](https://www.zigbee2mqtt.io/advanced/support-new-devices/02_support_new_tuya_devices.html)" : "", - "", - "If you need help with the process, feel free to ask here and we'll be happy to assist." - ].join('\n') - }); diff --git a/scripts/issueBot.mjs b/scripts/issueBot.mjs new file mode 100644 index 000000000..850579e4e --- /dev/null +++ b/scripts/issueBot.mjs @@ -0,0 +1,145 @@ +import {execSync} from "node:child_process"; + +export async function newDeviceSupport(github, _core, context, zhcDir) { + const issue = context.payload.issue; + // Hide previous bot comments + const comments = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + }); + + for (const comment of comments.data) { + if (comment.user.type === "Bot" && comment.user.login === "github-actions[bot]") { + await github.graphql(`mutation { + minimizeComment(input: {subjectId: "${comment.node_id}", classifier: OUTDATED}) { + clientMutationId + } +}`); + } + } + + // Check if Tuya manufacturer name is already supported. + const tuyaManufacturerNameRe = /['"](_T\w+_(\w+))['"]/g; + const tuyaManufacturerNames = Array.from(issue.body.matchAll(tuyaManufacturerNameRe), (m) => [m[1], m[2]]); + + if (tuyaManufacturerNames.length > 0) { + for (const [fullName, partialName] of tuyaManufacturerNames) { + const fullMatch = (() => { + try { + return execSync(`grep -r --include="*.ts" "${fullName}" "${zhcDir}"`, {encoding: "utf8"}); + } catch { + return undefined; + } + })(); + + if (fullMatch) { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + body: `👋 Hi there! The Tuya device with manufacturer name \`${fullName}\` is already supported in the latest dev branch. +See this [guide](https://www.zigbee2mqtt.io/advanced/more/switch-to-dev-branch.html) on how to update, after updating you can remove your external converter. + +In case you created the external converter with the goal to extend or fix an issue with the out-of-the-box support, please submit a pull request. +For instructions on how to create a pull request see the [docs](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html#_4-create-a-pull-request). +If you need help with the process, feel free to ask here and we'll be happy to assist.`, + }); + await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + state: "closed", + }); + + return; + } + + const partialMatch = (() => { + try { + return execSync(`grep -r --include="*.ts" "${partialName}" "${zhcDir}"`, {encoding: "utf8"}); + } catch { + return undefined; + } + })(); + + if (partialMatch) { + const candidates = Array.from(partialMatch.matchAll(tuyaManufacturerNameRe), (m) => m[1]); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + body: `👋 Hi there! A similar Tuya device of which the manufacturer name also ends with \`_${partialName}\` is already supported. +This means the device can probably be easily be supported by re-using the existing converter. + +I found the following matches: ${candidates.map((c) => `\`${c}\``).join(", ")} +Try to stop Z2M, change all occurrences of \`${fullName}\` in the \`data/database.db\` to one of the matches above and start Z2M. + +Let us know if it works so we can support this device out-of-the-box!`, + }); + + return; + } + } + } else { + // Check if zigbee model is already supported. + const zigbeeModelRe = /zigbeeModel: \[['"](.+)['"]\]/g; + const zigbeeModels = Array.from(issue.body.matchAll(zigbeeModelRe), (m) => m[1]); + + if (zigbeeModels.length > 0) { + for (const zigbeeModel of zigbeeModels) { + const fullMatch = (() => { + try { + return execSync(`grep -r --include="*.ts" '"${zigbeeModel}"' "${zhcDir}"`, {encoding: "utf8"}); + } catch { + return undefined; + } + })(); + + if (fullMatch) { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + body: `👋 Hi there! The device with zigbee model \`${zigbeeModel}\` is already supported in the latest dev branch. +See this [guide](https://www.zigbee2mqtt.io/advanced/more/switch-to-dev-branch.html) on how to update, after updating you can remove your external converter. + +In case you created the external converter with the goal to extend or fix an issue with the out-of-the-box support, please submit a pull request. +For instructions on how to create a pull request see the [docs](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html#_4-create-a-pull-request). + +If you need help with the process, feel free to ask here and we'll be happy to assist.`, + }); + await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + state: "closed", + }); + + return; + } + } + } + } + + // Create a request to pull request comment + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + body: `🙏 Thank you for creating this issue and sharing your external converter! + +In case all features work, please submit a pull request on this repository so the device can be supported out-of-the-box with the next release. +For instructions on how to create a pull request see the [docs](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html#_4-create-a-pull-request). + +If **NOT** all features work, please follow the [How To Support new devices](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html). +${ + tuyaManufacturerNames.length > 0 + ? "Since this is a Tuya also consider [How To Support new Tuya devices](https://www.zigbee2mqtt.io/advanced/support-new-devices/02_support_new_tuya_devices.html)." + : "" +} + +If you need help with the process, feel free to ask here and we'll be happy to assist.`, + }); +}