#!/usr/bin/env node /** * Script to create all Free Alberta campaigns from documentation * Run with: node scripts/create-campaigns.js */ const https = require('https'); // NocoDB Configuration const NOCODB_API_URL = 'https://db.freealberta.org'; const NOCODB_API_TOKEN = 'H3z5PEgvrC25LDRuvRpF1wuzsByG9PCem5DHyjPj'; const PROJECT_ID = 'plc0u50kgobr2xh'; const CAMPAIGNS_TABLE_ID = 'mh98emvhot9gjrg'; // Campaign definitions extracted from documentation const campaigns = [ // Free To Learn { slug: 'learning-access', title: 'Access to Learning', buttonText: 'Email Minister of Education', target: ['Provincial'] }, { slug: 'learn-glubish', title: 'Learning Policy Reform', buttonText: 'Email Minister Glubish', target: ['Provincial'] }, { slug: 'higher-learning', title: 'Higher Learning Access', buttonText: 'Email Minister of Advanced Education', target: ['Provincial'] }, // Free To Rest { slug: 'mental-health-rest', title: 'Mental Health & Rest', buttonText: 'Email Minister of Mental Health', target: ['Provincial'] }, { slug: 'rest-nixon', title: 'Work-Life Balance Policy', buttonText: 'Send Minister Nixon a Email', target: ['Provincial'] }, { slug: 'work-life', title: 'Work-Life Balance', buttonText: 'Email Minister of Jobs and Economy', target: ['Provincial'] }, // Free To Love { slug: 'love-smith', title: 'Community & Connection', buttonText: 'Email Premier Smith', target: ['Provincial'] }, { slug: 'love-ellis', title: 'Community Support', buttonText: 'Email Deputy Premier Ellis', target: ['Provincial'] }, { slug: 'love-turton', title: 'Community Programs', buttonText: 'Email Minister Turton', target: ['Provincial'] }, { slug: 'love-williams', title: 'Community Development', buttonText: 'Email Minister Williams', target: ['Provincial'] }, { slug: 'community-spaces', title: 'Community Spaces', buttonText: 'Email Minister of Arts and Culture', target: ['Provincial', 'Municipal'] }, { slug: 'public-spaces', title: 'Public Gathering Spaces', buttonText: 'Email Minister of Tourism', target: ['Provincial', 'Municipal'] }, // Free From Discrimination { slug: 'immigration-rights', title: 'Immigration Rights', buttonText: 'Email Minister of Immigration and Multiculturalism', target: ['Provincial', 'Federal'] }, { slug: 'gender-equity', title: 'Gender Equity', buttonText: 'Email Minister of Arts, Culture and Status of Women', target: ['Provincial'] }, // Free To Start Over { slug: 'career-transition', title: 'Career Transition Support', buttonText: 'Email Minister of Jobs and Economy', target: ['Provincial'] }, { slug: 'social-support', title: 'Social Support Services', buttonText: 'Email Minister of Seniors and Community Services', target: ['Provincial'] }, // Free From Surveillance { slug: 'privacy-rights', title: 'Privacy Rights', buttonText: 'Email the Premier', target: ['Provincial', 'Federal'] }, { slug: 'tech-privacy', title: 'Technology Privacy', buttonText: 'Email Minister of Technology and Innovation', target: ['Provincial'] }, // Free From State Violence { slug: 'human-rights', title: 'Human Rights Protection', buttonText: 'Email Human Rights Commission', target: ['Provincial'] }, { slug: 'ombudsman-investigation', title: 'Government Accountability', buttonText: 'Email Alberta Ombudsman', target: ['Provincial'] }, // Free To Create { slug: 'arts-support', title: 'Arts & Culture Support', buttonText: 'Email Minister of Arts and Culture', target: ['Provincial'] }, // Free From Colonization { slug: 'indigenous-relations', title: 'Indigenous Relations', buttonText: 'Email Minister of Indigenous Relations', target: ['Provincial', 'Federal'] }, { slug: 'land-use', title: 'Land Use Policy', buttonText: 'Email Minister of Municipal Affairs', target: ['Provincial', 'Municipal'] }, // Free Needs: Environment { slug: 'environment-protection', title: 'Environmental Protection', buttonText: 'Email Minister of Environment', target: ['Provincial'] }, { slug: 'clean-energy', title: 'Clean Energy Transition', buttonText: 'Email Minister of Energy', target: ['Provincial'] }, // Free To Associate { slug: 'association-rights-jobs', title: 'Worker Association Rights', buttonText: 'Email Minister of Jobs about Worker Associations', target: ['Provincial'] }, { slug: 'cultural-association-rights', title: 'Cultural Association Rights', buttonText: 'Email Minister of Immigration about Cultural Associations', target: ['Provincial', 'Federal'] }, // Free From Police Violence { slug: 'police-accountability', title: 'Police Accountability', buttonText: 'Email Minister of Public Safety', target: ['Provincial', 'Municipal'] }, { slug: 'asirt-investigation', title: 'Independent Police Investigations', buttonText: 'Email ASIRT Director', target: ['Provincial'] }, // Free Needs: Education { slug: 'education-reform', title: 'Education Reform', buttonText: 'Email Minister of Education', target: ['Provincial'] }, { slug: 'higher-education', title: 'Higher Education Access', buttonText: 'Email Minister of Advanced Education', target: ['Provincial'] }, // Free From Corruption { slug: 'anti-corruption', title: 'Anti-Corruption Measures', buttonText: 'Email Minister of Public Safety', target: ['Provincial'] }, { slug: 'legal-accountability', title: 'Legal System Accountability', buttonText: 'Email Minister of Justice', target: ['Provincial'] }, // Free To Gather { slug: 'assembly-rights', title: 'Freedom of Assembly', buttonText: 'Email Minister about Assembly Rights', target: ['Provincial'] }, { slug: 'gathering-spaces-municipal', title: 'Community Gathering Spaces', buttonText: 'Email Minister about Community Spaces', target: ['Provincial', 'Municipal'] }, // Free From Government Overreach { slug: 'government-reform', title: 'Government Reform', buttonText: 'Email the Premier', target: ['Provincial'] }, { slug: 'justice-reform', title: 'Justice System Reform', buttonText: 'Email Minister of Justice', target: ['Provincial'] }, // Free Needs: Housing { slug: 'seniors-housing', title: 'Seniors Housing', buttonText: 'Email Minister of Seniors and Community Services', target: ['Provincial'] }, { slug: 'family-housing', title: 'Family Housing Support', buttonText: 'Email Minister of Children and Family Services', target: ['Provincial'] }, // Free From Corporate Control { slug: 'economic-reform', title: 'Economic Reform', buttonText: 'Email Minister of Jobs and Economy', target: ['Provincial'] }, { slug: 'corporate-oversight', title: 'Corporate Oversight', buttonText: 'Email Minister of Trade and Tourism', target: ['Provincial'] }, // Free Needs: Water { slug: 'water-protection', title: 'Water Protection', buttonText: 'Email Minister of Environment', target: ['Provincial'] }, { slug: 'water-access', title: 'Water Access', buttonText: 'Email Minister of Seniors and Community Services', target: ['Provincial', 'Municipal'] }, // Free Needs: Healthcare { slug: 'health-reform', title: 'Healthcare Reform', buttonText: 'Email Minister of Health', target: ['Provincial'] }, { slug: 'mental-health', title: 'Mental Health Services', buttonText: 'Email Minister of Mental Health and Addiction', target: ['Provincial'] }, // Free Needs: Air Quality { slug: 'air-quality-protection', title: 'Air Quality Protection', buttonText: 'Email Minister about Air Quality Protection', target: ['Provincial'] }, { slug: 'air-quality-health', title: 'Air Quality Health Impacts', buttonText: 'Email Minister about Health Impacts', target: ['Provincial'] }, // Land Back { slug: 'landback', title: 'Land Back Movement', buttonText: 'Send Email to Minister Responsible for Landback', target: ['Provincial', 'Federal'] }, { slug: 'land-rights', title: 'Indigenous Land Rights', buttonText: 'Email Minister of Indigenous Relations', target: ['Provincial', 'Federal'] }, { slug: 'protected-areas', title: 'Protected Areas', buttonText: 'Email Minister of Forestry and Parks', target: ['Provincial'] }, // Free Needs: Transportation { slug: 'transport-reform', title: 'Transportation Reform', buttonText: 'Email Minister of Transportation', target: ['Provincial'] }, { slug: 'municipal-transit', title: 'Municipal Transit', buttonText: 'Email Minister of Municipal Affairs', target: ['Provincial', 'Municipal'] }, // Free Needs: Food { slug: 'food-security', title: 'Food Security', buttonText: 'Email Minister of Agriculture', target: ['Provincial'] }, { slug: 'food-access', title: 'Food Access', buttonText: 'Email Minister of Seniors and Community Services', target: ['Provincial'] }, // Free Things: Communications { slug: 'tech-innovation', title: 'Technology & Innovation', buttonText: 'Email Minister of Technology and Innovation', target: ['Provincial'] }, { slug: 'telecom-reform', title: 'Telecommunications Reform', buttonText: 'Email Minister of Service Alberta', target: ['Provincial', 'Federal'] }, // Free Things: Energy { slug: 'energy-reform', title: 'Energy Policy Reform', buttonText: 'Email Minister of Energy and Minerals', target: ['Provincial'] }, { slug: 'utilities-reform', title: 'Utilities Reform', buttonText: 'Email Minister of Affordability and Utilities', target: ['Provincial'] }, // Free Things: Public Services { slug: 'public-services-reform', title: 'Public Services Reform', buttonText: 'Email Minister of Service Alberta', target: ['Provincial'] }, { slug: 'public-services-vision', title: 'Public Services Vision', buttonText: 'Email the Premier', target: ['Provincial'] }, // Free Things: Recreation { slug: 'recreation-tourism', title: 'Recreation & Tourism', buttonText: 'Email Minister of Tourism and Sport', target: ['Provincial'] }, { slug: 'recreation-parks', title: 'Parks & Recreation', buttonText: 'Email Minister of Forestry and Parks', target: ['Provincial'] }, // Archive (for completeness) { slug: 'startover', title: 'Starting Over Support', buttonText: 'Email Minister Jones', target: ['Provincial'] }, ]; // Generate campaign descriptions based on title function generateDescription(campaign) { return `Take action! ${campaign.buttonText} to advocate for ${campaign.title.toLowerCase()} in Alberta. Your voice matters - let your elected representatives know that Albertans care about this issue.`; } // Generate email subject function generateEmailSubject(campaign) { return `Constituent Request: ${campaign.title}`; } // Generate email body function generateEmailBody(campaign) { return `Dear [Representative Name], I am writing to you as a concerned constituent regarding ${campaign.title.toLowerCase()}. This issue is important to me and many other Albertans. I urge you to take meaningful action to address this matter and represent the interests of your constituents. I would appreciate a response outlining your position and any steps you plan to take. Thank you for your service to our community. Sincerely, [Your Name] [Your Postal Code]`; } // Create a campaign via NocoDB API async function createCampaign(campaign) { const data = { 'Campaign Slug': campaign.slug, 'Campaign Title': campaign.title, 'Description': generateDescription(campaign), 'Email Subject': generateEmailSubject(campaign), 'Email Body': generateEmailBody(campaign), 'Call to Action': campaign.buttonText, 'Status': 'active', 'Allow SMTP Email': true, 'Allow Mailto Link': true, 'Collect User Info': true, 'Show Email Count': true, 'Show Call Count': true, 'Allow Email Editing': true, 'Allow Custom Recipients': false, 'Show Response Wall Button': false, 'Target Government Levels': campaign.target, }; return new Promise((resolve, reject) => { const postData = JSON.stringify(data); const options = { hostname: 'db.freealberta.org', port: 443, path: `/api/v1/db/data/v1/${PROJECT_ID}/${CAMPAIGNS_TABLE_ID}`, method: 'POST', headers: { 'Content-Type': 'application/json', 'xc-token': NOCODB_API_TOKEN, 'Content-Length': Buffer.byteLength(postData) } }; const req = https.request(options, (res) => { let responseData = ''; res.on('data', (chunk) => { responseData += chunk; }); res.on('end', () => { if (res.statusCode >= 200 && res.statusCode < 300) { resolve({ success: true, slug: campaign.slug, data: JSON.parse(responseData) }); } else { reject(new Error(`Failed to create ${campaign.slug}: ${res.statusCode} - ${responseData}`)); } }); }); req.on('error', (error) => { reject(error); }); req.write(postData); req.end(); }); } // Main execution async function main() { console.log(`Creating ${campaigns.length} campaigns...`); console.log('='.repeat(50)); let successCount = 0; let failCount = 0; for (const campaign of campaigns) { try { const result = await createCampaign(campaign); console.log(`✓ Created: ${campaign.slug} - "${campaign.title}"`); successCount++; // Small delay to avoid rate limiting await new Promise(resolve => setTimeout(resolve, 100)); } catch (error) { console.error(`✗ Failed: ${campaign.slug} - ${error.message}`); failCount++; } } console.log('='.repeat(50)); console.log(`Done! Created: ${successCount}, Failed: ${failCount}`); console.log(`\nCampaigns are available at: https://influence.freealberta.org/campaign/{slug}`); } main().catch(console.error);