Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- Polymarket Commission Email Filter v2 - Google Apps Script
- SETUP
- 1. Create a project at https://script.google.com/ and paste this entire file into Code.gs.
- 2. Set LIMIT_DOLLARS to your preferred threshold (default: $10).
- 3. In the editor, go to Services > + > Gmail API > Add.
- 4. Run archiveSmallCommissions once and authorize Gmail access.
- 5. Add one hourly time-driven trigger for archiveSmallCommissions.
- If you already have this trigger, keep it instead of adding a duplicate.
- 6. For a standard Google Cloud project, also enable the Gmail API in its console.
- BEHAVIOR
- - Archives matching inbox messages with commissions strictly below the threshold.
- - Reads the sender and subject; leaves message bodies unread by this script.
- - Inspects at most 100 messages per run, with at least one second between API calls.
- - Uses a hidden Gmail label to remember inspected messages, including ones kept
- in the inbox. Changing the threshold creates a new marker for future checks.
- - Retries temporary API failures with increasing delays, then defers remaining
- work to the next scheduled run. Other errors remain visible as failures.
- - Stops early to leave time before the Apps Script execution limit.
- - To inspect a marked message again at the same threshold, remove its
- PolymarketChecked_v2_<threshold in cents> label in Gmail.
- - This code does not send email or transmit mailbox data to third-party services.
- */
- const LIMIT_DOLLARS = 10;
- const MAX_MESSAGES_PER_RUN = 100;
- const MIN_API_INTERVAL_MS = 1000;
- const MAX_RUN_TIME_MS = 240000;
- const MAX_API_RETRIES = 4;
- function archiveSmallCommissions() {
- const limitCents = Math.round(LIMIT_DOLLARS * 100);
- if (typeof LIMIT_DOLLARS !== 'number' || !Number.isSafeInteger(limitCents) || limitCents < 0) {
- throw new Error('LIMIT_DOLLARS must be a non-negative dollar amount.');
- }
- const lock = LockService.getScriptLock();
- if (!lock.tryLock(1000)) return;
- const deadline = Date.now() + MAX_RUN_TIME_MS;
- const pause = new Error('Resume remaining work on the next scheduled run.');
- let nextCallAt = 0;
- let checked = 0;
- let archived = 0;
- // Pace every API attempt, including retries and label operations.
- function callGmail(request) {
- for (let attempt = 0; attempt <= MAX_API_RETRIES; attempt++) {
- const wait = Math.max(0, nextCallAt - Date.now());
- if (Date.now() + wait + 10000 >= deadline) throw pause;
- if (wait > 0) Utilities.sleep(wait);
- nextCallAt = Date.now() + MIN_API_INTERVAL_MS;
- try {
- return request();
- } catch (error) {
- const text = String(error && error.message || error);
- const temporary = /quota exceeded|rate.?limit.?exceeded|too many requests|\b429\b|backend.?error|service unavailable|internal error|\b50[0234]\b/i.test(text);
- if (!temporary) throw error;
- if (attempt === MAX_API_RETRIES) throw pause;
- const delay = Math.min(32000, 2000 * Math.pow(2, attempt)) +
- Math.floor(Math.random() * 1000);
- if (Date.now() + delay + 10000 >= deadline) throw pause;
- Utilities.sleep(delay);
- }
- }
- }
- function wasRemoved(error) {
- return /\b404\b|not found|requested entity was not found/i.test(
- String(error && error.message || error)
- );
- }
- try {
- const labelName = 'PolymarketChecked_v2_' + limitCents;
- const labels = callGmail(() => Gmail.Users.Labels.list('me')).labels || [];
- let marker = labels.find(label => label.name === labelName);
- if (!marker) {
- marker = callGmail(() => Gmail.Users.Labels.create({
- name: labelName,
- labelListVisibility: 'labelHide',
- messageListVisibility: 'hide'
- }, 'me'));
- }
- // Read one bounded page before modifying any messages. The marker excludes
- // completed work on the next run, so there is no shifting-page pagination.
- const page = callGmail(() => Gmail.Users.Messages.list('me', {
- q: `in:inbox from:${DUB_SENDER} subject:commission subject:Polymarket -label:${labelName}`,
- maxResults: MAX_MESSAGES_PER_RUN
- }));
- for (const item of page.messages || []) {
- if (Date.now() + 15000 >= deadline) throw pause;
- let message;
- try {
- message = callGmail(() => Gmail.Users.Messages.get('me', item.id, {
- format: 'metadata',
- metadataHeaders: ['Subject', 'From']
- }));
- } catch (error) {
- // A message may have been deleted since the list request.
- if (wasRemoved(error)) continue;
- throw error;
- }
- if (!(message.labelIds || []).includes('INBOX')) continue;
- const headers = (message.payload || {}).headers || [];
- const getHeader = name =>
- (headers.find(h => h.name.toLowerCase() === name) || {}).value || '';
- const from = getHeader('from').trim();
- const address = (from.match(/<([^<>]+)>/) || [null, from])[1];
- let shouldArchive = false;
- // Require the exact sender and a recognized commission subject.
- // Unrecognized emails stay in the inbox and receive only the marker.
- if (address.trim().toLowerCase() === DUB_SENDER) {
- const match = getHeader('subject').trim().match(
- /^You['’]ve made a \$([0-9]+(?:,[0-9]{3})*)(?:\.([0-9]{2}))? commission from Polymarket$/i
- );
- if (match) {
- const cents = Number(match[1].replace(/,/g, '')) * 100 +
- Number(match[2] || '0');
- shouldArchive = Number.isSafeInteger(cents) && cents < limitCents;
- }
- }
- const changes = { addLabelIds: [marker.id] };
- if (shouldArchive) changes.removeLabelIds = ['INBOX'];
- try {
- // Archive and record completion in the same idempotent operation.
- callGmail(() => Gmail.Users.Messages.modify(changes, 'me', item.id));
- } catch (error) {
- if (wasRemoved(error)) continue;
- throw error;
- }
- checked++;
- if (shouldArchive) archived++;
- }
- } catch (error) {
- if (error !== pause) throw error;
- console.warn('Run paused. Remaining messages will be retried on the next scheduled run.');
- } finally {
- console.log(`Messages checked: ${checked}. Messages archived: ${archived}.`);
- lock.releaseLock();
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment