diff --git a/.github/scripts/check-eol-newrelease.js b/.github/scripts/check-eol-newrelease.js new file mode 100644 index 00000000..ca664ca0 --- /dev/null +++ b/.github/scripts/check-eol-newrelease.js @@ -0,0 +1,232 @@ +// @ts-check + +/** + * @typedef {object} ReleaseCycle + * @property {string|number} cycle - Release cycle (e.g. "1.20", 8.1, etc.) + * @property {string} [releaseDate] - YYYY-MM-DD string for the first release in this cycle + * @property {string|boolean} [eol] - End of Life date (YYYY-MM-DD) or false if not EoL + * @property {string} [latest] - Latest release in this cycle + * @property {string|null} [link] - Link to changelog or similar, if available + * @property {boolean|string} [lts] - Whether this cycle is non-LTS (false), or LTS starting on a given date + * @property {string|boolean} [support] - Active support date (YYYY-MM-DD) or boolean + * @property {string|boolean} [discontinued] - Discontinued date (YYYY-MM-DD) or boolean + */ + +/** + * @typedef {object} EolNewReleaseConfig + * @property {string} languageName + * @property {string} eolJsonUrl + * @property {string} eolViewUrl + * @property {number} eolLookbackDays + * @property {number} newReleaseThresholdDays + * @property {boolean} ltsOnly + * @property {number} retryCount + * @property {number} retryIntervalSec + */ + +/** + * This script checks EoL and new releases from endoflife.date JSON. + * It creates Issues for: + * - EoL reached within eolLookbackDays + * - New releases within newReleaseThresholdDays + * If fetching fails after multiple retries, an error Issue is created once per week. + * + * @param {import('@actions/github-script').AsyncFunctionArguments} actionCtx + * @param {EolNewReleaseConfig} config + */ +module.exports = async function checkEolAndNewReleases(actionCtx, config) { + const { github, context, core } = actionCtx; + const { + languageName, + eolJsonUrl, + eolViewUrl, + eolLookbackDays, + newReleaseThresholdDays, + ltsOnly, + retryCount, + retryIntervalSec, + } = config; + + /** + * Returns a simple "year-week" string like "2025-W09". + * This is a rough calculation (not strictly ISO-8601). + * @param {Date} date + * @returns {string} + */ + const getYearWeek = (date) => { + const startOfYear = new Date(date.getFullYear(), 0, 1); + const dayOfYear = Math.floor((date - startOfYear) / 86400000) + 1; + const weekNum = Math.ceil(dayOfYear / 7); + return `${date.getFullYear()}-W${String(weekNum).padStart(2, '0')}`; + }; + + /** + * Simple dedent function. + * Removes common leading indentation based on the minimum indent across all lines. + * Also trims empty lines at the start/end. + * @param {string} str + * @returns {string} + */ + const dedent = (str) => { + const lines = str.split('\n'); + while (lines.length && lines[0].trim() === '') lines.shift(); + while (lines.length && lines[lines.length - 1].trim() === '') lines.pop(); + + /** @type {number[]} */ + const indents = lines + .filter(line => line.trim() !== '') + .map(line => (line.match(/^(\s+)/)?.[1].length) ?? 0); + + const minIndent = indents.length > 0 ? Math.min(...indents) : 0; + return lines.map(line => line.slice(minIndent)).join('\n'); + }; + + /** + * Creates an Issue if an Issue with the same title does not exist (state=all). + * @param {string} title + * @param {string} body + * @param {string[]} [labels] + * @returns {Promise} true if created, false if an Issue with same title already exists + */ + const createIssueIfNotExists = async (title, body, labels = []) => { + const issues = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'all', + per_page: 100, + }); + const found = issues.data.find(i => i.title === title); + if (found) { + core.info(`Issue already exists: "${title}"`); + return false; + } + await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title, + body, + labels, + }); + core.notice(`Created Issue: "${title}"`); + return true; + }; + + /** + * Fetch with retry, returning an array of ReleaseCycle objects. + * @param {string} url + * @returns {Promise} + */ + const fetchWithRetry = async (url) => { + let lastErr = null; + for (let i = 1; i <= retryCount; i++) { + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`HTTP ${response.status} ${response.statusText}`); + } + /** @type {ReleaseCycle[]} */ + const jsonData = await response.json(); + return jsonData; + } catch (err) { + lastErr = err; + core.warning(`Fetch failed (attempt ${i}/${retryCount}): ${err.message}`); + if (i < retryCount) { + await new Promise(r => setTimeout(r, retryIntervalSec * 1000)); + } + } + } + throw new Error(`Failed to fetch after ${retryCount} attempts: ${lastErr?.message}`); + }; + + /** + * Check EoL for a single release. + * @param {ReleaseCycle} release + * @param {Date} now + * @param {Date} eolLookbackDate + */ + const checkEoL = async (release, now, eolLookbackDate) => { + if (ltsOnly && release.lts === false) { + core.info(`Skipping non-LTS release: ${release.cycle}`); + return; + } + if (typeof release.eol === 'string') { + const eolDate = new Date(release.eol); + if (!isNaN(eolDate.getTime())) { + // Check if it reached EoL within the last eolLookbackDays + if (eolDate <= now && eolDate >= eolLookbackDate) { + if (!release.cycle) return; + const title = `[EoL] ${languageName} ${release.cycle} reached End of Life!`; + const body = dedent(` + **EoL date**: ${release.eol} + endoflife.date for ${languageName}: ${eolViewUrl} + + This version has reached End of Life. + Please consider drop support or update as needed. + `); + await createIssueIfNotExists(title, body, ['keep']); + } + } + } + }; + + /** + * Check new release for a single release. + * @param {ReleaseCycle} release + * @param {Date} now + * @param {Date} newReleaseSince + */ + const checkNewRelease = async (release, now, newReleaseSince) => { + if (ltsOnly && release.lts === false) { + core.info(`Skipping non-LTS release: ${release.cycle}`); + return; + } + if (typeof release.releaseDate === 'string') { + const rDate = new Date(release.releaseDate); + if (!isNaN(rDate.getTime())) { + // Check if releaseDate is within newReleaseThresholdDays + if (rDate >= newReleaseSince && rDate <= now) { + if (!release.cycle) return; + const title = `[New Release] ${languageName} ${release.cycle} is now available!`; + const body = dedent(` + **Release date**: ${release.releaseDate} + endoflife.date for ${languageName}: ${eolViewUrl} + + A new version has been released. + Please consider updating or testing as needed. + `); + await createIssueIfNotExists(title, body, ['keep']); + } + } + } + }; + + core.info(`Starting EoL & NewRelease check for ${languageName} ...`); + const now = new Date(); + const eolLookbackDate = new Date(now); + eolLookbackDate.setDate(eolLookbackDate.getDate() - eolLookbackDays); + + const newReleaseSince = new Date(now); + newReleaseSince.setDate(newReleaseSince.getDate() - newReleaseThresholdDays); + + try { + const data = await fetchWithRetry(eolJsonUrl); + for (const release of data) { + core.info(`Checking release: ${JSON.stringify(release)}`); + await checkEoL(release, now, eolLookbackDate); + await checkNewRelease(release, now, newReleaseSince); + } + } catch (err) { + core.error(`Error checking EoL/NewReleases for ${languageName}: ${err.message}`); + const yw = getYearWeek(new Date()); + const errorTitle = `[CI ERROR] EoL/NewRelease check for ${languageName} in ${yw}`; + const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + const body = dedent(` + The automated check for EoL and new releases failed (retried ${retryCount} times). + **Error**: ${err.message} + **Action URL**: [View job log here](${runUrl}) + Please investigate (network issues, invalid JSON, etc.) and fix it to monitor EOL site automatically. + `); + await createIssueIfNotExists(errorTitle, body, ['keep']); + core.setFailed(err.message); + } +}; diff --git a/.github/workflows/check-eol-newrelease.yml b/.github/workflows/check-eol-newrelease.yml new file mode 100644 index 00000000..ebc30eef --- /dev/null +++ b/.github/workflows/check-eol-newrelease.yml @@ -0,0 +1,31 @@ +name: "Check EoL & New Releases" + +on: + schedule: + # Every day at 22:00 UTC -> 07:00 JST + - cron: '0 22 * * *' + workflow_dispatch: + +jobs: + check-go: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Run EoL & NewRelease check + uses: actions/github-script@v7 + with: + script: | + const checkEolAndNewReleases = require('.github/scripts/check-eol-newrelease.js'); + await checkEolAndNewReleases({ github, context, core }, { + languageName: 'Node.js', + eolJsonUrl: 'https://endoflife.date/api/nodejs.json', + eolViewUrl: 'https://endoflife.date/nodejs', + eolLookbackDays: 1000, + newReleaseThresholdDays: 1000, + ltsOnly: true, + retryCount: 3, + retryIntervalSec: 30 + }); + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/line-openapi b/line-openapi index 5ae51b6a..4e10c73d 160000 --- a/line-openapi +++ b/line-openapi @@ -1 +1 @@ -Subproject commit 5ae51b6a91aea5da364666cdc60d24919ad0b6ad +Subproject commit 4e10c73df579b4c1821a305f0190dfbe8710d2de diff --git a/linebot/manage_audience/.openapi-generator/FILES b/linebot/manage_audience/.openapi-generator/FILES index aff9cf9a..6df2811c 100644 --- a/linebot/manage_audience/.openapi-generator/FILES +++ b/linebot/manage_audience/.openapi-generator/FILES @@ -1,5 +1,6 @@ api_manage_audience.go api_manage_audience_blob.go +model_adaccount.go model_add_audience_to_audience_group_request.go model_audience.go model_audience_group.go @@ -19,10 +20,13 @@ model_create_click_based_audience_group_request.go model_create_click_based_audience_group_response.go model_create_imp_based_audience_group_request.go model_create_imp_based_audience_group_response.go +model_detailed_owner.go model_error_detail.go model_error_response.go model_get_audience_data_response.go model_get_audience_group_authority_level_response.go model_get_audience_groups_response.go +model_get_shared_audience_data_response.go +model_get_shared_audience_groups_response.go model_update_audience_group_authority_level_request.go model_update_audience_group_description_request.go diff --git a/linebot/manage_audience/api_manage_audience.go b/linebot/manage_audience/api_manage_audience.go index b77736c2..7539a321 100644 --- a/linebot/manage_audience/api_manage_audience.go +++ b/linebot/manage_audience/api_manage_audience.go @@ -764,6 +764,183 @@ func (client *ManageAudienceAPI) GetAudienceGroupsWithHttpInfo( } +// GetSharedAudienceData +// +// Gets audience data. +// Parameters: +// audienceGroupId The audience ID. + +// https://developers.line.biz/en/reference/messaging-api/#get-shared-audience +func (client *ManageAudienceAPI) GetSharedAudienceData( + + audienceGroupId int64, + +) (*GetSharedAudienceDataResponse, error) { + _, body, error := client.GetSharedAudienceDataWithHttpInfo( + + audienceGroupId, + ) + return body, error +} + +// GetSharedAudienceData +// If you want to take advantage of the HTTPResponse object for status codes and headers, use this signature. +// +// Gets audience data. +// Parameters: +// audienceGroupId The audience ID. + +// https://developers.line.biz/en/reference/messaging-api/#get-shared-audience +func (client *ManageAudienceAPI) GetSharedAudienceDataWithHttpInfo( + + audienceGroupId int64, + +) (*http.Response, *GetSharedAudienceDataResponse, error) { + path := "/v2/bot/audienceGroup/shared/{audienceGroupId}" + + path = strings.Replace(path, "{audienceGroupId}", strconv.FormatInt(audienceGroupId, 10), -1) + + req, err := http.NewRequest(http.MethodGet, client.Url(path), nil) + if err != nil { + return nil, nil, err + } + + res, err := client.Do(req) + + if err != nil { + return res, nil, err + } + + if res.StatusCode/100 != 2 { + bodyBytes, err := io.ReadAll(res.Body) + bodyReader := bytes.NewReader(bodyBytes) + if err != nil { + return res, nil, fmt.Errorf("failed to read response body: %w", err) + } + res.Body = io.NopCloser(bodyReader) + return res, nil, fmt.Errorf("unexpected status code: %d, %s", res.StatusCode, string(bodyBytes)) + } + + defer res.Body.Close() + + decoder := json.NewDecoder(res.Body) + result := GetSharedAudienceDataResponse{} + if err := decoder.Decode(&result); err != nil { + return res, nil, fmt.Errorf("failed to decode JSON: %w", err) + } + return res, &result, nil + +} + +// GetSharedAudienceGroups +// +// Gets data for more than one audience, including those shared by the Business Manager. +// Parameters: +// page The page to return when getting (paginated) results. Must be 1 or higher. +// description The name of the audience(s) to return. You can search for partial matches. This is case-insensitive, meaning AUDIENCE and audience are considered identical. If omitted, the name of the audience(s) will not be used as a search criterion. +// status The status of the audience(s) to return. If omitted, the status of the audience(s) will not be used as a search criterion. +// size The number of audiences per page. Default: 20 Max: 40 +// createRoute How the audience was created. If omitted, all audiences are included. `OA_MANAGER`: Return only audiences created with LINE Official Account Manager (opens new window). `MESSAGING_API`: Return only audiences created with Messaging API. + +// https://developers.line.biz/en/reference/messaging-api/#get-shared-audience-list +func (client *ManageAudienceAPI) GetSharedAudienceGroups( + + page int64, + + description string, + + status AudienceGroupStatus, + + size int64, + + createRoute AudienceGroupCreateRoute, + +) (*GetSharedAudienceGroupsResponse, error) { + _, body, error := client.GetSharedAudienceGroupsWithHttpInfo( + + page, + + description, + + status, + + size, + + createRoute, + ) + return body, error +} + +// GetSharedAudienceGroups +// If you want to take advantage of the HTTPResponse object for status codes and headers, use this signature. +// +// Gets data for more than one audience, including those shared by the Business Manager. +// Parameters: +// page The page to return when getting (paginated) results. Must be 1 or higher. +// description The name of the audience(s) to return. You can search for partial matches. This is case-insensitive, meaning AUDIENCE and audience are considered identical. If omitted, the name of the audience(s) will not be used as a search criterion. +// status The status of the audience(s) to return. If omitted, the status of the audience(s) will not be used as a search criterion. +// size The number of audiences per page. Default: 20 Max: 40 +// createRoute How the audience was created. If omitted, all audiences are included. `OA_MANAGER`: Return only audiences created with LINE Official Account Manager (opens new window). `MESSAGING_API`: Return only audiences created with Messaging API. + +// https://developers.line.biz/en/reference/messaging-api/#get-shared-audience-list +func (client *ManageAudienceAPI) GetSharedAudienceGroupsWithHttpInfo( + + page int64, + + description string, + + status AudienceGroupStatus, + + size int64, + + createRoute AudienceGroupCreateRoute, + +) (*http.Response, *GetSharedAudienceGroupsResponse, error) { + path := "/v2/bot/audienceGroup/shared/list" + + req, err := http.NewRequest(http.MethodGet, client.Url(path), nil) + if err != nil { + return nil, nil, err + } + + query := url.Values{} + query.Add("page", strconv.FormatInt(page, 10)) + if description != "" { + query.Add("description", description) + } + query.Add("status", string(status)) + query.Add("size", strconv.FormatInt(size, 10)) + query.Add("createRoute", string(createRoute)) + + req.URL.RawQuery = query.Encode() + + res, err := client.Do(req) + + if err != nil { + return res, nil, err + } + + if res.StatusCode/100 != 2 { + bodyBytes, err := io.ReadAll(res.Body) + bodyReader := bytes.NewReader(bodyBytes) + if err != nil { + return res, nil, fmt.Errorf("failed to read response body: %w", err) + } + res.Body = io.NopCloser(bodyReader) + return res, nil, fmt.Errorf("unexpected status code: %d, %s", res.StatusCode, string(bodyBytes)) + } + + defer res.Body.Close() + + decoder := json.NewDecoder(res.Body) + result := GetSharedAudienceGroupsResponse{} + if err := decoder.Decode(&result); err != nil { + return res, nil, fmt.Errorf("failed to decode JSON: %w", err) + } + return res, &result, nil + +} + // UpdateAudienceGroupAuthorityLevel // // Change the authority level of the audience diff --git a/linebot/manage_audience/model_adaccount.go b/linebot/manage_audience/model_adaccount.go new file mode 100644 index 00000000..9789b04c --- /dev/null +++ b/linebot/manage_audience/model_adaccount.go @@ -0,0 +1,31 @@ +/** + * LINE Messaging API + * This document describes LINE Messaging API. + * + * The version of the OpenAPI document: 0.0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +//go:generate python3 ../../generate-code.py +package manage_audience + +// Adaccount +// Adaccount + +type Adaccount struct { + + /** + * Ad account name. + */ + Name string `json:"name,omitempty"` +} diff --git a/linebot/manage_audience/model_detailed_owner.go b/linebot/manage_audience/model_detailed_owner.go new file mode 100644 index 00000000..aea15a94 --- /dev/null +++ b/linebot/manage_audience/model_detailed_owner.go @@ -0,0 +1,41 @@ +/** + * LINE Messaging API + * This document describes LINE Messaging API. + * + * The version of the OpenAPI document: 0.0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +//go:generate python3 ../../generate-code.py +package manage_audience + +// DetailedOwner +// Owner of this audience group. + +type DetailedOwner struct { + + /** + * Service name where the audience group has been created. + */ + ServiceType string `json:"serviceType,omitempty"` + + /** + * Owner ID in the service. + */ + Id string `json:"id,omitempty"` + + /** + * Owner account name. + */ + Name string `json:"name,omitempty"` +} diff --git a/linebot/manage_audience/model_get_audience_data_response.go b/linebot/manage_audience/model_get_audience_data_response.go index 59456c59..3ff4cea6 100644 --- a/linebot/manage_audience/model_get_audience_data_response.go +++ b/linebot/manage_audience/model_get_audience_data_response.go @@ -33,4 +33,9 @@ type GetAudienceDataResponse struct { * An array of jobs. This array is used to keep track of each attempt to add new user IDs or IFAs to an audience for uploading user IDs. Empty array is returned for any other type of audience. Max: 50 */ Jobs []AudienceGroupJob `json:"jobs,omitempty"` + + /** + * Get Adaccount + */ + Adaccount *Adaccount `json:"adaccount,omitempty"` } diff --git a/linebot/manage_audience/model_get_shared_audience_data_response.go b/linebot/manage_audience/model_get_shared_audience_data_response.go new file mode 100644 index 00000000..d83995a0 --- /dev/null +++ b/linebot/manage_audience/model_get_shared_audience_data_response.go @@ -0,0 +1,41 @@ +/** + * LINE Messaging API + * This document describes LINE Messaging API. + * + * The version of the OpenAPI document: 0.0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +//go:generate python3 ../../generate-code.py +package manage_audience + +// GetSharedAudienceDataResponse +// Get audience data +// https://developers.line.biz/en/reference/messaging-api/#get-audience-group +type GetSharedAudienceDataResponse struct { + + /** + * Get AudienceGroup + */ + AudienceGroup *AudienceGroup `json:"audienceGroup,omitempty"` + + /** + * An array of jobs. This array is used to keep track of each attempt to add new user IDs or IFAs to an audience for uploading user IDs. Empty array is returned for any other type of audience. Max: 50 + */ + Jobs []AudienceGroupJob `json:"jobs,omitempty"` + + /** + * Get Owner + */ + Owner *DetailedOwner `json:"owner,omitempty"` +} diff --git a/linebot/manage_audience/model_get_shared_audience_groups_response.go b/linebot/manage_audience/model_get_shared_audience_groups_response.go new file mode 100644 index 00000000..790243c1 --- /dev/null +++ b/linebot/manage_audience/model_get_shared_audience_groups_response.go @@ -0,0 +1,56 @@ +/** + * LINE Messaging API + * This document describes LINE Messaging API. + * + * The version of the OpenAPI document: 0.0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +//go:generate python3 ../../generate-code.py +package manage_audience + +// GetSharedAudienceGroupsResponse +// Gets data for more than one audience. +// https://developers.line.biz/en/reference/messaging-api/#get-audience-groups +type GetSharedAudienceGroupsResponse struct { + + /** + * An array of audience data. If there are no audiences that match the specified filter, an empty array will be returned. + */ + AudienceGroups []AudienceGroup `json:"audienceGroups,omitempty"` + + /** + * true when this is not the last page. + */ + HasNextPage bool `json:"hasNextPage"` + + /** + * The total number of audiences that can be returned with the specified filter. + */ + TotalCount int64 `json:"totalCount"` + + /** + * Of the audiences you can get with the specified filter, the number of audiences with the update permission set to READ_WRITE. + */ + ReadWriteAudienceGroupTotalCount int64 `json:"readWriteAudienceGroupTotalCount"` + + /** + * The current page number. + */ + Page int64 `json:"page"` + + /** + * The maximum number of audiences on the current page. + */ + Size int64 `json:"size"` +} diff --git a/linebot/messaging_api/.openapi-generator/FILES b/linebot/messaging_api/.openapi-generator/FILES index 5574c12e..a0560e26 100644 --- a/linebot/messaging_api/.openapi-generator/FILES +++ b/linebot/messaging_api/.openapi-generator/FILES @@ -63,6 +63,7 @@ model_gender_demographic_filter.go model_get_aggregation_unit_name_list_response.go model_get_aggregation_unit_usage_response.go model_get_followers_response.go +model_get_joined_membership_users_response.go model_get_membership_subscription_response.go model_get_message_content_transcoding_response.go model_get_webhook_endpoint_response.go diff --git a/linebot/messaging_api/api_messaging_api.go b/linebot/messaging_api/api_messaging_api.go index 387fafc6..c8f61991 100644 --- a/linebot/messaging_api/api_messaging_api.go +++ b/linebot/messaging_api/api_messaging_api.go @@ -511,77 +511,6 @@ func (client *MessagingApiAPI) DeleteRichMenuAliasWithHttpInfo( } -// GetAdPhoneMessageStatistics -// -// Get result of message delivery using phone number -// Parameters: -// date Date the message was sent Format: `yyyyMMdd` (e.g. `20190831`) Time Zone: UTC+9 - -// https://developers.line.biz/en/reference/partner-docs/#get-phone-audience-match -func (client *MessagingApiAPI) GetAdPhoneMessageStatistics( - - date string, - -) (*NumberOfMessagesResponse, error) { - _, body, error := client.GetAdPhoneMessageStatisticsWithHttpInfo( - - date, - ) - return body, error -} - -// GetAdPhoneMessageStatistics -// If you want to take advantage of the HTTPResponse object for status codes and headers, use this signature. -// -// Get result of message delivery using phone number -// Parameters: -// date Date the message was sent Format: `yyyyMMdd` (e.g. `20190831`) Time Zone: UTC+9 - -// https://developers.line.biz/en/reference/partner-docs/#get-phone-audience-match -func (client *MessagingApiAPI) GetAdPhoneMessageStatisticsWithHttpInfo( - - date string, - -) (*http.Response, *NumberOfMessagesResponse, error) { - path := "/v2/bot/message/delivery/ad_phone" - - req, err := http.NewRequest(http.MethodGet, client.Url(path), nil) - if err != nil { - return nil, nil, err - } - - query := url.Values{} - query.Add("date", date) - - req.URL.RawQuery = query.Encode() - - res, err := client.Do(req) - - if err != nil { - return res, nil, err - } - - if res.StatusCode/100 != 2 { - bodyBytes, err := io.ReadAll(res.Body) - bodyReader := bytes.NewReader(bodyBytes) - if err != nil { - return res, nil, fmt.Errorf("failed to read response body: %w", err) - } - res.Body = io.NopCloser(bodyReader) - return res, nil, fmt.Errorf("unexpected status code: %d, %s", res.StatusCode, string(bodyBytes)) - } - - defer res.Body.Close() - - decoder := json.NewDecoder(res.Body) - result := NumberOfMessagesResponse{} - if err := decoder.Decode(&result); err != nil { - return res, nil, fmt.Errorf("failed to decode JSON: %w", err) - } - return res, &result, nil - -} - // GetAggregationUnitNameList // // Get name list of units used this month @@ -1204,6 +1133,98 @@ func (client *MessagingApiAPI) GetGroupSummaryWithHttpInfo( } +// GetJoinedMembershipUsers +// +// Get a list of user IDs who joined the membership. +// Parameters: +// membershipId Membership plan ID. +// start A continuation token to get next remaining membership user IDs. Returned only when there are remaining user IDs that weren't returned in the userIds property in the previous request. The continuation token expires in 24 hours (86,400 seconds). +// limit The max number of items to return for this API call. The value is set to 300 by default, but the max acceptable value is 1000. + +// https://developers.line.biz/en/reference/messaging-api/#get-membership-user-ids +func (client *MessagingApiAPI) GetJoinedMembershipUsers( + + membershipId int32, + + start string, + + limit int32, + +) (*GetJoinedMembershipUsersResponse, error) { + _, body, error := client.GetJoinedMembershipUsersWithHttpInfo( + + membershipId, + + start, + + limit, + ) + return body, error +} + +// GetJoinedMembershipUsers +// If you want to take advantage of the HTTPResponse object for status codes and headers, use this signature. +// +// Get a list of user IDs who joined the membership. +// Parameters: +// membershipId Membership plan ID. +// start A continuation token to get next remaining membership user IDs. Returned only when there are remaining user IDs that weren't returned in the userIds property in the previous request. The continuation token expires in 24 hours (86,400 seconds). +// limit The max number of items to return for this API call. The value is set to 300 by default, but the max acceptable value is 1000. + +// https://developers.line.biz/en/reference/messaging-api/#get-membership-user-ids +func (client *MessagingApiAPI) GetJoinedMembershipUsersWithHttpInfo( + + membershipId int32, + + start string, + + limit int32, + +) (*http.Response, *GetJoinedMembershipUsersResponse, error) { + path := "/v2/bot/membership/{membershipId}/users/ids" + + path = strings.Replace(path, "{membershipId}", strconv.FormatInt(int64(membershipId), 10), -1) + + req, err := http.NewRequest(http.MethodGet, client.Url(path), nil) + if err != nil { + return nil, nil, err + } + + query := url.Values{} + if start != "" { + query.Add("start", start) + } + query.Add("limit", strconv.FormatInt(int64(limit), 10)) + + req.URL.RawQuery = query.Encode() + + res, err := client.Do(req) + + if err != nil { + return res, nil, err + } + + if res.StatusCode/100 != 2 { + bodyBytes, err := io.ReadAll(res.Body) + bodyReader := bytes.NewReader(bodyBytes) + if err != nil { + return res, nil, fmt.Errorf("failed to read response body: %w", err) + } + res.Body = io.NopCloser(bodyReader) + return res, nil, fmt.Errorf("unexpected status code: %d, %s", res.StatusCode, string(bodyBytes)) + } + + defer res.Body.Close() + + decoder := json.NewDecoder(res.Body) + result := GetJoinedMembershipUsersResponse{} + if err := decoder.Decode(&result); err != nil { + return res, nil, fmt.Errorf("failed to decode JSON: %w", err) + } + return res, &result, nil + +} + // GetMembershipList // // Get a list of memberships. diff --git a/linebot/messaging_api/model_get_joined_membership_users_response.go b/linebot/messaging_api/model_get_joined_membership_users_response.go new file mode 100644 index 00000000..e0787957 --- /dev/null +++ b/linebot/messaging_api/model_get_joined_membership_users_response.go @@ -0,0 +1,36 @@ +/** + * LINE Messaging API + * This document describes LINE Messaging API. + * + * The version of the OpenAPI document: 0.0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +//go:generate python3 ../../generate-code.py +package messaging_api + +// GetJoinedMembershipUsersResponse +// List of users who have joined the membership +// https://developers.line.biz/en/reference/messaging-api/#get-membership-user-ids +type GetJoinedMembershipUsersResponse struct { + + /** + * A list of user IDs who joined the membership. Users who have not agreed to the bot user agreement, are not following the bot, or are not active will be excluded. If there are no users in the membership, an empty list will be returned. (Required) + */ + UserIds []string `json:"userIds"` + + /** + * A continuation token to get next remaining membership user IDs. Returned only when there are remaining user IDs that weren't returned in the userIds property in the previous request. The continuation token expires in 24 hours (86,400 seconds). + */ + Next string `json:"next,omitempty"` +} diff --git a/linebot/webhook/.openapi-generator/FILES b/linebot/webhook/.openapi-generator/FILES index 81094156..7aebf05f 100644 --- a/linebot/webhook/.openapi-generator/FILES +++ b/linebot/webhook/.openapi-generator/FILES @@ -25,13 +25,17 @@ model_image_message_content.go model_image_set.go model_join_event.go model_joined_members.go +model_joined_membership_content.go model_leave_event.go model_left_members.go +model_left_membership_content.go model_link_content.go model_link_things_content.go model_location_message_content.go model_member_joined_event.go model_member_left_event.go +model_membership_content.go +model_membership_event.go model_mention.go model_mentionee.go model_message_content.go @@ -42,6 +46,7 @@ model_pnp_delivery.go model_pnp_delivery_completion_event.go model_postback_content.go model_postback_event.go +model_renewed_membership_content.go model_room_source.go model_scenario_result.go model_scenario_result_things_content.go diff --git a/linebot/webhook/model_event.go b/linebot/webhook/model_event.go index 6b7eef05..a16a4355 100644 --- a/linebot/webhook/model_event.go +++ b/linebot/webhook/model_event.go @@ -164,6 +164,16 @@ func setDiscriminatorPropertyEvent(r EventInterface) EventInterface { v.Type = "memberLeft" } return v + case *MembershipEvent: + if v.Type == "" { + v.Type = "membership" + } + return v + case MembershipEvent: + if v.Type == "" { + v.Type = "membership" + } + return v case *MessageEvent: if v.Type == "" { v.Type = "message" @@ -347,6 +357,12 @@ func UnmarshalEvent(data []byte) (EventInterface, error) { return nil, fmt.Errorf("UnmarshalEvent: Cannot read memberLeft: %w", err) } return memberLeft, nil + case "membership": + var membership MembershipEvent + if err := json.Unmarshal(data, &membership); err != nil { + return nil, fmt.Errorf("UnmarshalEvent: Cannot read membership: %w", err) + } + return membership, nil case "message": var message MessageEvent if err := json.Unmarshal(data, &message); err != nil { diff --git a/linebot/webhook/model_joined_membership_content.go b/linebot/webhook/model_joined_membership_content.go new file mode 100644 index 00000000..1e372066 --- /dev/null +++ b/linebot/webhook/model_joined_membership_content.go @@ -0,0 +1,51 @@ +/** + * Webhook Type Definition + * Webhook event definition of the LINE Messaging API + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +//go:generate python3 ../../generate-code.py +package webhook + +import ( + "encoding/json" +) + +// JoinedMembershipContent +// JoinedMembershipContent + +type JoinedMembershipContent struct { + MembershipContent + + /** + * The ID of the membership that the user joined. This is defined for each membership. (Required) + */ + MembershipId int32 `json:"membershipId"` +} + +// MarshalJSON customizes the JSON serialization of the JoinedMembershipContent struct. +func (r *JoinedMembershipContent) MarshalJSON() ([]byte, error) { + + type Alias JoinedMembershipContent + return json.Marshal(&struct { + *Alias + + Type string `json:"type"` + }{ + Alias: (*Alias)(r), + + Type: "joined", + }) +} diff --git a/linebot/webhook/model_left_membership_content.go b/linebot/webhook/model_left_membership_content.go new file mode 100644 index 00000000..212107d1 --- /dev/null +++ b/linebot/webhook/model_left_membership_content.go @@ -0,0 +1,51 @@ +/** + * Webhook Type Definition + * Webhook event definition of the LINE Messaging API + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +//go:generate python3 ../../generate-code.py +package webhook + +import ( + "encoding/json" +) + +// LeftMembershipContent +// LeftMembershipContent + +type LeftMembershipContent struct { + MembershipContent + + /** + * The ID of the membership that the user left. This is defined for each membership. (Required) + */ + MembershipId int32 `json:"membershipId"` +} + +// MarshalJSON customizes the JSON serialization of the LeftMembershipContent struct. +func (r *LeftMembershipContent) MarshalJSON() ([]byte, error) { + + type Alias LeftMembershipContent + return json.Marshal(&struct { + *Alias + + Type string `json:"type"` + }{ + Alias: (*Alias)(r), + + Type: "left", + }) +} diff --git a/linebot/webhook/model_membership_content.go b/linebot/webhook/model_membership_content.go new file mode 100644 index 00000000..24d8a965 --- /dev/null +++ b/linebot/webhook/model_membership_content.go @@ -0,0 +1,131 @@ +/** + * Webhook Type Definition + * Webhook event definition of the LINE Messaging API + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +//go:generate python3 ../../generate-code.py +package webhook + +import ( + "encoding/json" + "fmt" +) + +type MembershipContentInterface interface { + GetType() string +} + +func (e MembershipContent) GetType() string { + return e.Type +} + +type UnknownMembershipContent struct { + MembershipContentInterface + Type string + Raw map[string]json.RawMessage +} + +func (e UnknownMembershipContent) GetType() string { + return e.Type +} + +func setDiscriminatorPropertyMembershipContent(r MembershipContentInterface) MembershipContentInterface { + switch v := r.(type) { + case *JoinedMembershipContent: + if v.Type == "" { + v.Type = "joined" + } + return v + case JoinedMembershipContent: + if v.Type == "" { + v.Type = "joined" + } + return v + case *LeftMembershipContent: + if v.Type == "" { + v.Type = "left" + } + return v + case LeftMembershipContent: + if v.Type == "" { + v.Type = "left" + } + return v + case *RenewedMembershipContent: + if v.Type == "" { + v.Type = "renewed" + } + return v + case RenewedMembershipContent: + if v.Type == "" { + v.Type = "renewed" + } + return v + + default: + return v + } +} + +// MembershipContent +// Content of the membership event. + +type MembershipContent struct { + // Type of membership event. + + Type string `json:"type"` +} + +func UnmarshalMembershipContent(data []byte) (MembershipContentInterface, error) { + var raw map[string]json.RawMessage + err := json.Unmarshal(data, &raw) + if err != nil { + return nil, fmt.Errorf("UnmarshalMembershipContent: %w", err) + } + + var discriminator string + err = json.Unmarshal(raw["type"], &discriminator) + if err != nil { + return nil, fmt.Errorf("UnmarshalMembershipContent: Cannot read type: %w", err) + } + + switch discriminator { + case "joined": + var joined JoinedMembershipContent + if err := json.Unmarshal(data, &joined); err != nil { + return nil, fmt.Errorf("UnmarshalMembershipContent: Cannot read joined: %w", err) + } + return joined, nil + case "left": + var left LeftMembershipContent + if err := json.Unmarshal(data, &left); err != nil { + return nil, fmt.Errorf("UnmarshalMembershipContent: Cannot read left: %w", err) + } + return left, nil + case "renewed": + var renewed RenewedMembershipContent + if err := json.Unmarshal(data, &renewed); err != nil { + return nil, fmt.Errorf("UnmarshalMembershipContent: Cannot read renewed: %w", err) + } + return renewed, nil + + default: + var unknown UnknownMembershipContent + unknown.Type = discriminator + unknown.Raw = raw + return unknown, nil + } +} diff --git a/linebot/webhook/model_membership_event.go b/linebot/webhook/model_membership_event.go new file mode 100644 index 00000000..e8804493 --- /dev/null +++ b/linebot/webhook/model_membership_event.go @@ -0,0 +1,174 @@ +/** + * Webhook Type Definition + * Webhook event definition of the LINE Messaging API + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +//go:generate python3 ../../generate-code.py +package webhook + +import ( + "encoding/json" + "fmt" +) + +// MembershipEvent +// This event indicates that a user has subscribed (joined), unsubscribed (left), or renewed the bot's membership. + +type MembershipEvent struct { + Event + + /** + * Get Source + */ + Source SourceInterface `json:"source,omitempty"` + + /** + * Time of the event in milliseconds. (Required) + */ + Timestamp int64 `json:"timestamp"` + + /** + * Get Mode + */ + Mode EventMode `json:"mode"` + + /** + * Webhook Event ID. An ID that uniquely identifies a webhook event. This is a string in ULID format. (Required) + */ + WebhookEventId string `json:"webhookEventId"` + + /** + * Get DeliveryContext + */ + DeliveryContext *DeliveryContext `json:"deliveryContext"` + + /** + * Reply token used to send reply message to this event (Required) + */ + ReplyToken string `json:"replyToken"` + + /** + * Get Membership + */ + Membership MembershipContentInterface `json:"membership"` +} + +func (cr *MembershipEvent) UnmarshalJSON(data []byte) error { + var raw map[string]json.RawMessage + err := json.Unmarshal(data, &raw) + if err != nil { + return fmt.Errorf("JSON parse error in map: %w", err) + } + + if raw["type"] != nil { + + err = json.Unmarshal(raw["type"], &cr.Type) + if err != nil { + return fmt.Errorf("JSON parse error in string(Type): %w", err) + } + + } + + if raw["source"] != nil { + + if rawsource, ok := raw["source"]; ok && rawsource != nil { + Source, err := UnmarshalSource(rawsource) + if err != nil { + return fmt.Errorf("JSON parse error in Source(discriminator): %w", err) + } + cr.Source = Source + } + + } + + if raw["timestamp"] != nil { + + err = json.Unmarshal(raw["timestamp"], &cr.Timestamp) + if err != nil { + return fmt.Errorf("JSON parse error in int64(Timestamp): %w", err) + } + + } + + if raw["mode"] != nil { + + err = json.Unmarshal(raw["mode"], &cr.Mode) + if err != nil { + return fmt.Errorf("JSON parse error in EventMode(Mode): %w", err) + } + + } + + if raw["webhookEventId"] != nil { + + err = json.Unmarshal(raw["webhookEventId"], &cr.WebhookEventId) + if err != nil { + return fmt.Errorf("JSON parse error in string(WebhookEventId): %w", err) + } + + } + + if raw["deliveryContext"] != nil { + + err = json.Unmarshal(raw["deliveryContext"], &cr.DeliveryContext) + if err != nil { + return fmt.Errorf("JSON parse error in DeliveryContext(DeliveryContext): %w", err) + } + + } + + if raw["replyToken"] != nil { + + err = json.Unmarshal(raw["replyToken"], &cr.ReplyToken) + if err != nil { + return fmt.Errorf("JSON parse error in string(ReplyToken): %w", err) + } + + } + + if raw["membership"] != nil { + + if rawmembership, ok := raw["membership"]; ok && rawmembership != nil { + Membership, err := UnmarshalMembershipContent(rawmembership) + if err != nil { + return fmt.Errorf("JSON parse error in MembershipContent(discriminator): %w", err) + } + cr.Membership = Membership + } + + } + + return nil +} + +// MarshalJSON customizes the JSON serialization of the MembershipEvent struct. +func (r *MembershipEvent) MarshalJSON() ([]byte, error) { + + r.Source = setDiscriminatorPropertySource(r.Source) + + r.Membership = setDiscriminatorPropertyMembershipContent(r.Membership) + + type Alias MembershipEvent + return json.Marshal(&struct { + *Alias + + Type string `json:"type"` + }{ + Alias: (*Alias)(r), + + Type: "membership", + }) +} diff --git a/linebot/webhook/model_renewed_membership_content.go b/linebot/webhook/model_renewed_membership_content.go new file mode 100644 index 00000000..8a72655b --- /dev/null +++ b/linebot/webhook/model_renewed_membership_content.go @@ -0,0 +1,51 @@ +/** + * Webhook Type Definition + * Webhook event definition of the LINE Messaging API + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +//go:generate python3 ../../generate-code.py +package webhook + +import ( + "encoding/json" +) + +// RenewedMembershipContent +// RenewedMembershipContent + +type RenewedMembershipContent struct { + MembershipContent + + /** + * The ID of the membership that the user renewed. This is defined for each membership. (Required) + */ + MembershipId int32 `json:"membershipId"` +} + +// MarshalJSON customizes the JSON serialization of the RenewedMembershipContent struct. +func (r *RenewedMembershipContent) MarshalJSON() ([]byte, error) { + + type Alias RenewedMembershipContent + return json.Marshal(&struct { + *Alias + + Type string `json:"type"` + }{ + Alias: (*Alias)(r), + + Type: "renewed", + }) +}