Skip to content

Instantly share code, notes, and snippets.

@fcrespo82
Last active October 31, 2020 12:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fcrespo82/2a06ff0b9f3492ea14e7ff7f5181c26c to your computer and use it in GitHub Desktop.
Save fcrespo82/2a06ff0b9f3492ea14e7ff7f5181c26c to your computer and use it in GitHub Desktop.
Schedule WhatsApp messages
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: deep-green; icon-glyph: comment-alt;
// If you need to send message from another app customize the url here
function getMessageURL(item) {
return `https://wa.me/${item.number}/?text=${encodeURIComponent(item.message)}`
}
async function createTable(data) {
const table = new UITable()
table.showSeparators = true
populateTable(table, data)
await table.present(true)
}
function populateTable(table, data, removeAllFirst) {
if (removeAllFirst) {
table.removeAllRows()
}
const addScheduleRow = new UITableRow()
const scheduleButton = addScheduleRow.addButton("Add new scheduled message")
scheduleButton.centerAligned()
scheduleButton.onTap = async () => {
const schedule = await newSchedule()
data.push(schedule)
populateTable(table, data, true)
saveScheduleMessages(data)
table.reload()
}
table.addRow(addScheduleRow)
for (i in data) {
let item = data[i]
let row = new UITableRow()
let date = new Date()
date.setTime(item.date)
if (date < Date.now()) {
row.backgroundColor = new Color("aaffaaff")
}
let textCell = row.addText(item.name, item.message)
textCell.widthWeight = 80
let sendButton = row.addButton("📤")
sendButton.widthWeight = 10
sendButton.onTap = () => {
let url = getMessageURL(item)
Safari.open(url)
}
let removeButton = row.addButton("❌")
removeButton.widthWeight = 10
removeButton.onTap = async () => {
const index = data.indexOf(item)
data.splice(index, 1)
populateTable(table, data, true)
let notifications = await Notification.allPending()
notifications.re
saveScheduleMessages(data)
table.reload()
}
table.addRow(row)
}
}
async function chooseContact() {
return new Promise(async (resolve, reject) => {
let containers = await ContactsContainer.all()
let contacts = await Contact.all(containers)
contacts = contacts.sort((a, b) => {
let compA = a.organizationName
let compB = b.organizationName
if (a.familyName.trim() != "") {
compA = a.familyName
}
if (b.familyName.trim() != "") {
compB = b.familyName
}
if (a.givenName.trim() != "") {
compA = a.givenName
}
if (b.givenName.trim() != "") {
compB = b.givenName
}
return compA.trim().toLowerCase().localeCompare(compB.trim().toLowerCase())
})
let tableRows = []
contacts.forEach((contact) => {
let name = `${contact.organizationName.trim()} - `
if (contact.givenName.trim() != "") {
name = `${contact.givenName.trim()} ${contact.familyName.trim()}`.trim()
}
contact.phoneNumbers.forEach(async (phoneNumber) => {
tableRows.push({
name: name,
number: phoneNumber.value.replace(/\D/g, '')
})
})
})
const table = new UITable()
table.showSeparators = true
tableRows.forEach((contact) => {
const row = new UITableRow()
row.height = 50
let cell = row.addText(contact.name, contact.number)
cell.subtitleColor = Color.gray()
table.addRow(row)
row.onSelect = async (index) => {
tableRows[index].number = await cleanUpNumber(tableRows[index].number)
resolve(tableRows[index])
}
})
table.present(true)
})
}
async function newSchedule() {
const contact = await chooseContact()
const picker = new DatePicker()
let minimumDate = new Date()
// At least 2 minutes delay
minimumDate.setTime(minimumDate.getTime() + (1000 * 120))
minimumDate.setTime(minimumDate.getTime() + (1000 * 30))
picker.minimumDate = minimumDate
const date = await picker.pickDateAndTime()
let getText = new Alert();
getText.addTextField("message");
getText.title = `Enter message to send to ${contact.name}`;
getText.addAction("OK");
await getText.present();
const message = getText.textFieldValue(0);
const item = { ...contact, date: date.getTime(), message }
await createNotification(item)
return item
}
function getIdentifier(item) {
return `br.com.crespo.schedule-whatsapp.${item.id}`
}
async function createNotification(item) {
const notification = new Notification()
notification.identifier = getIdentifier(item)
notification.sound = "default"
notification.title = "Send message"
notification.body = `to ${item.name}`
notification.openURL = getMessageURL(item)
notification.addAction("Send", getMessageURL(item))
let triggerDate = new Date()
triggerDate.setTime(item.date)
notification.setTriggerDate(triggerDate)
await notification.schedule()
}
async function cleanUpNumber(number) {
let getText = new Alert();
getText.addTextField("message", number.replace(/\D/g, ''));
getText.title = `Fix number?`;
getText.message = "WhatsApp needs a number in full international number Use 15551234567 instead of +001-(555)1234567"
getText.addAction("OK");
await getText.present();
return getText.textFieldValue(0)
}
// Widget related functions
async function createWidget(data) {
const widget = new ListWidget()
await populateWidget(widget, data)
const newSchedule = widget.addStack()
newSchedule.addSpacer()
const text = newSchedule.addText("Schedule new")
text.url = "scriptable:///run?scriptName=Schedule%20WhatsApp"
widget.presentMedium()
Script.setWidget(widget)
return widget
}
function populateWidget(widget, data) {
const maxItems = 2
let itemsIncluded = 0
for (let i in data) {
let stack = widget.addStack()
stack.layoutVertically()
let item = data[i]
let text = stack.addText(`${item.name} - ${item.number}`)
let date = new Date()
date.setTime(item.date)
if (date < Date.now()) {
continue
}
const dateFormatter = new DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy HH:mm:ss"
let dateItem = stack.addText(dateFormatter.string(date))
text.url = getMessageURL(item)
widget.addSpacer()
itemsIncluded += 1
if (itemsIncluded === maxItems) {
break
}
}
}
// File related functions
async function loadScheduleMessages() {
const fm = FileManager.iCloud()
const path = getFilePath()
if (fm.fileExists(path)) {
await fm.downloadFileFromiCloud(path)
const text = fm.readString(path)
return JSON.parse(text)
}
}
function saveScheduleMessages(data) {
const fm = FileManager.iCloud()
const path = getFilePath()
fm.writeString(path, JSON.stringify(data, null, 4))
}
function getFilePath() {
let fm = FileManager.iCloud()
let dirPath = fm.joinPath(
fm.documentsDirectory(),
"br.com.crespo.schedule-whatsapp")
if (!fm.fileExists(dirPath)) {
fm.createDirectory(dirPath)
fm.writeString(fm.joinPath(dirPath, "messages.json"), JSON.stringify([], null, 4))
}
return fm.joinPath(dirPath, "messages.json")
}
let data = await loadScheduleMessages()
if (config.runsInApp) {
createTable(data)
} else if (config.runsInWidget) {
createWidget(data)
} else if (config.runsWithSiri) {
createWidget(data)
}
Script.complete()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment