[{"data":1,"prerenderedAt":1396},["ShallowReactive",2],{"navigation_docs":3,"-plugins":36,"-plugins-surround":1393},[4,8,12,16,20,24,28,32],{"title":5,"path":6,"stem":7},"Set up your server","\u002Fsetup","1.setup",{"title":9,"path":10,"stem":11},"Addresses and domains","\u002Faddresses","2.addresses",{"title":13,"path":14,"stem":15},"Sign-in and discovery","\u002Fsign-in","3.sign-in",{"title":17,"path":18,"stem":19},"Mail apps","\u002Fproxy","4.proxy",{"title":21,"path":22,"stem":23},"Spam","\u002Fspam","5.spam",{"title":25,"path":26,"stem":27},"Plugins","\u002Fplugins","6.plugins",{"title":29,"path":30,"stem":31},"Configuration","\u002Fconfiguration","7.configuration",{"title":33,"path":34,"stem":35},"Troubleshooting","\u002Ftroubleshooting","8.troubleshooting",{"id":37,"title":25,"body":38,"description":79,"extension":1386,"links":1387,"meta":1388,"navigation":1389,"path":26,"seo":1390,"stem":27,"__hash__":1392},"docs\u002F6.plugins.md",{"type":39,"value":40,"toc":1370},"minimark",[41,50,62,67,70,73,96,106,129,135,239,242,257,263,266,271,274,283,286,289,293,302,305,320,327,331,346,360,364,378,385,394,400,403,448,457,460,466,472,504,507,510,514,533,686,696,710,716,719,1018,1124,1131,1142,1201,1207,1213,1219,1279,1282,1332,1345,1349,1366],[42,43,44,45,49],"p",{},"A plugin is a Worker you deploy that your server tells about mail as it arrives and as it goes out. A plugin can watch, and if you let it, it can file received mail or stop a send. You run a plugin's own commands through ",[46,47,48],"code",{},"pnpm mailopus with \u003Cplugin> ...",".",[42,51,52,61],{},[53,54,58],"a",{"href":55,"rel":56},"https:\u002F\u002Fgithub.com\u002Fmailopus\u002Fserver\u002Fblob\u002Fmain\u002Fexamples\u002Fgotmail\u002FREADME.md",[57],"nofollow",[46,59,60],{},"examples\u002Fgotmail"," is a working one. It alerts you by Slack, webhook, text message or a sound when mail you're waiting for arrives.",[63,64,66],"h2",{"id":65},"connect-a-plugin","Connect a plugin",[42,68,69],{},"A plugin runs as its own Worker in the same Cloudflare account as your server. Your server reaches it over a service binding, so the plugin has no public address and needs no key. Nothing outside your account can call it.",[42,71,72],{},"Deploy the plugin first, following its README. Then, from your Mailopus clone:",[74,75,80],"pre",{"className":76,"code":77,"language":78,"meta":79,"style":79},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pnpm mailopus plugin add gotmail\npnpm run deploy\n","sh","",[46,81,82,90],{"__ignoreMap":79},[83,84,87],"span",{"class":85,"line":86},"line",1,[83,88,89],{},"pnpm mailopus plugin add gotmail\n",[83,91,93],{"class":85,"line":92},2,[83,94,95],{},"pnpm run deploy\n",[42,97,98,101,102,105],{},[46,99,100],{},"plugin add"," does two things. It records the plugin in your database, and it adds a service binding to ",[46,103,104],{},"wrangler.jsonc",":",[74,107,111],{"className":108,"code":109,"language":110,"meta":79,"style":79},"language-jsonc shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\"services\": [\n    { \"binding\": \"PLUGIN_GOTMAIL\", \"service\": \"mailopus-gotmail\", \"entrypoint\": \"Plugin\" }\n],\n","jsonc",[46,112,113,118,123],{"__ignoreMap":79},[83,114,115],{"class":85,"line":86},[83,116,117],{},"\"services\": [\n",[83,119,120],{"class":85,"line":92},[83,121,122],{},"    { \"binding\": \"PLUGIN_GOTMAIL\", \"service\": \"mailopus-gotmail\", \"entrypoint\": \"Plugin\" }\n",[83,124,126],{"class":85,"line":125},3,[83,127,128],{},"],\n",[42,130,131,132,134],{},"The deploy turns the binding on. If your server deploys from GitHub, as it does after the Deploy button, commit and push ",[46,133,104],{}," instead, or the next build drops the binding. The deploy fails if the plugin's Worker doesn't exist yet, which is why the plugin goes first.",[136,137,138,154],"table",{},[139,140,141],"thead",{},[142,143,144,148,151],"tr",{},[145,146,147],"th",{},"Option",[145,149,150],{},"Default",[145,152,153],{},"What it does",[155,156,157,175,190,203,222],"tbody",{},[142,158,159,165,170],{},[160,161,162],"td",{},[46,163,164],{},"--service \u003Cworker>",[160,166,167],{},[46,168,169],{},"mailopus-\u003Cname>",[160,171,172,173],{},"The plugin Worker's name, from its own ",[46,174,104],{},[142,176,177,182,187],{},[160,178,179],{},[46,180,181],{},"--binding \u003CNAME>",[160,183,184],{},[46,185,186],{},"PLUGIN_\u003CNAME>",[160,188,189],{},"The binding's name on your server",[142,191,192,197,200],{},[160,193,194],{},[46,195,196],{},"--events receive,send",[160,198,199],{},"both",[160,201,202],{},"Which events the plugin hears about",[142,204,205,210,215],{},[160,206,207],{},[46,208,209],{},"--wants envelope|headers|body",[160,211,212],{},[46,213,214],{},"envelope",[160,216,217,218,49],{},"How much of each message it sees. See ",[53,219,221],{"href":220},"#what-a-plugin-sees","What a plugin sees",[142,223,224,229,232],{},[160,225,226],{},[46,227,228],{},"--act",[160,230,231],{},"off",[160,233,234,235,49],{},"The server waits for the plugin's answer and follows it. See ",[53,236,238],{"href":237},"#plugins-that-act","Plugins that act",[42,240,241],{},"Give a plugin only what its README says it needs. Adding the same name again replaces its entry, so that is also how you change an option.",[74,243,245],{"className":76,"code":244,"language":78,"meta":79,"style":79},"pnpm mailopus plugin list\npnpm mailopus plugin remove gotmail\n",[46,246,247,252],{"__ignoreMap":79},[83,248,249],{"class":85,"line":86},[83,250,251],{},"pnpm mailopus plugin list\n",[83,253,254],{"class":85,"line":92},[83,255,256],{},"pnpm mailopus plugin remove gotmail\n",[42,258,259,260,262],{},"Removing a plugin stops the events at once. Its binding stays in ",[46,261,104],{}," and its Worker stays deployed. Take the binding out and deploy before you delete the plugin's Worker, or your next deploy fails.",[42,264,265],{},"Until the binding is deployed, your server finds no plugin behind the name and carries on without it. Mail is not held up.",[267,268,270],"h3",{"id":269},"a-plugin-somewhere-else","A plugin somewhere else",[42,272,273],{},"A plugin that can't live in your account can be reached over https with a key:",[74,275,277],{"className":76,"code":276,"language":78,"meta":79,"style":79},"pnpm mailopus plugin add gotmail https:\u002F\u002Fmailopus-gotmail.\u003Cyour subdomain>.workers.dev\n",[46,278,279],{"__ignoreMap":79},[83,280,281],{"class":85,"line":86},[83,282,276],{},[42,284,285],{},"It prints a key. Set that key on the plugin as the secret its README names. No binding and no deploy of your server are needed.",[42,287,288],{},"Prefer the binding. Over https the plugin has a public address guarded by one key, that key sits in your database in plain text so the command line can use it, and message data leaves your account for wherever the URL points. Use this only for a plugin you run and trust.",[63,290,292],{"id":291},"test-a-plugin","Test a plugin",[74,294,296],{"className":76,"code":295,"language":78,"meta":79,"style":79},"pnpm mailopus plugin test gotmail\n",[46,297,298],{"__ignoreMap":79},[83,299,300],{"class":85,"line":86},[83,301,295],{},[42,303,304],{},"This sends the plugin a made-up received message and prints its answer. No mail is stored or sent. Shape the message to match what you're testing:",[74,306,308],{"className":76,"code":307,"language":78,"meta":79,"style":79},"pnpm mailopus plugin test gotmail receive --from thedude72@gmail.com --subject \"The rug\"\npnpm mailopus plugin test gotmail send --to hr@corpo.example\n",[46,309,310,315],{"__ignoreMap":79},[83,311,312],{"class":85,"line":86},[83,313,314],{},"pnpm mailopus plugin test gotmail receive --from thedude72@gmail.com --subject \"The rug\"\n",[83,316,317],{"class":85,"line":92},[83,318,319],{},"pnpm mailopus plugin test gotmail send --to hr@corpo.example\n",[42,321,322,323,326],{},"The event carries ",[46,324,325],{},"\"test\": true",", so a plugin can tell it from real mail.",[63,328,330],{"id":329},"run-a-plugins-commands","Run a plugin's commands",[74,332,334],{"className":76,"code":333,"language":78,"meta":79,"style":79},"pnpm mailopus with gotmail help\npnpm mailopus with gotmail once receive --from thedude72 slack\n",[46,335,336,341],{"__ignoreMap":79},[83,337,338],{"class":85,"line":86},[83,339,340],{},"pnpm mailopus with gotmail help\n",[83,342,343],{"class":85,"line":92},[83,344,345],{},"pnpm mailopus with gotmail once receive --from thedude72 slack\n",[42,347,348,349,352,353,356,357,49],{},"Everything after the plugin's name goes to the plugin as you typed it. Each plugin defines its own commands, and ",[46,350,351],{},"help"," is the convention for listing them. Put ",[46,354,355],{},"-c \u003Cfile>"," before ",[46,358,359],{},"with",[267,361,363],{"id":362},"how-the-command-line-reaches-a-bound-plugin","How the command line reaches a bound plugin",[42,365,366,367,369,370,373,374,377],{},"A bound plugin has no address, so ",[46,368,359],{}," and ",[46,371,372],{},"plugin test"," go through your server, which passes them over the binding. To prove the call is yours, the command line first writes a random token's hash into your database, which takes your Cloudflare login. Your server accepts that token for five minutes. This is the same root of trust as ",[46,375,376],{},"pnpm mailopus invite",": whoever holds the cloud account is the admin.",[42,379,380,381,384],{},"The command line finds your server through your domain's ",[46,382,383],{},"_mailopus"," TXT record. If that record isn't there, name the server before the command:",[74,386,388],{"className":76,"code":387,"language":78,"meta":79,"style":79},"pnpm mailopus --server https:\u002F\u002Fmailopus.\u003Cyour subdomain>.workers.dev with gotmail list\n",[46,389,390],{"__ignoreMap":79},[83,391,392],{"class":85,"line":86},[83,393,387],{},[42,395,396,399],{},[46,397,398],{},"MAILOPUS_SERVER"," does the same.",[63,401,221],{"id":402},"what-a-plugin-sees",[136,404,405,417],{},[139,406,407],{},[142,408,409,414],{},[145,410,411],{},[46,412,413],{},"--wants",[145,415,416],{},"The plugin gets",[155,418,419,428,438],{},[142,420,421,425],{},[160,422,423],{},[46,424,214],{},[160,426,427],{},"From, To, Cc, subject, Message-ID, date, and for received mail the delivery address and whether it went to Junk",[142,429,430,435],{},[160,431,432],{},[46,433,434],{},"headers",[160,436,437],{},"The envelope and the full header block",[142,439,440,445],{},[160,441,442],{},[46,443,444],{},"body",[160,446,447],{},"The envelope, the headers and the plain text of the message",[42,449,450,451,453,454,49],{},"No plugin gets attachments or the raw message. The default is ",[46,452,214],{},", and the body leaves your server only for a plugin you added with ",[46,455,456],{},"--wants body",[63,458,238],{"id":459},"plugins-that-act",[42,461,462,463,465],{},"Without ",[46,464,228],{}," a plugin only watches. Your server tells it after a received message is stored and after a send goes out, and ignores what it answers.",[42,467,468,469,471],{},"With ",[46,470,228],{}," the server asks the plugin first and waits up to 1.5 seconds:",[473,474,475,498],"ul",{},[476,477,478,482,483,486,487,490,491,494,495,49],"li",{},[479,480,481],"strong",{},"On receive",", the plugin can send the message to Junk or set keywords such as ",[46,484,485],{},"$seen"," (read) and ",[46,488,489],{},"$flagged"," (starred). A message a plugin junked shows ",[46,492,493],{},"plugin:\u003Cname>"," as its reason in ",[46,496,497],{},"pnpm mailopus junk \u003Caddress>",[476,499,500,503],{},[479,501,502],{},"On send",", the plugin can stop the message. Nobody receives it, and the app shows the plugin's reason as the send error. A scheduled send that a plugin stops goes back to Drafts.",[42,505,506],{},"A plugin can never lose mail. If it's slow, down, or answers with something the server doesn't understand, the server carries on as if it had said nothing: received mail is delivered and sends go out. Don't use a plugin as the only guard on something that must never be sent.",[42,508,509],{},"Plugins that act are asked one after another in the order you added them, so each one can add up to 1.5 seconds to a delivery or a send.",[63,511,513],{"id":512},"write-a-plugin","Write a plugin",[42,515,516,517,520,521,524,525,528,529,532],{},"A plugin is a Worker that answers two ",[46,518,519],{},"POST"," requests. Export a ",[46,522,523],{},"WorkerEntrypoint"," class named ",[46,526,527],{},"Plugin"," with a ",[46,530,531],{},"fetch"," method. Your server's binding names that entrypoint, and only a Worker you bound in your own account can call it, so it checks no key:",[74,534,538],{"className":535,"code":536,"language":537,"meta":79,"style":79},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { WorkerEntrypoint } from \"cloudflare:workers\";\n\nexport class Plugin extends WorkerEntrypoint\u003CEnv> {\n    override fetch(request: Request): Promise\u003CResponse> {\n        return handle(request, this.env);\n    }\n}\n","ts",[46,539,540,573,579,609,646,674,680],{"__ignoreMap":79},[83,541,542,546,550,554,557,560,563,567,570],{"class":85,"line":86},[83,543,545],{"class":544},"s7zQu","import",[83,547,549],{"class":548},"sMK4o"," {",[83,551,553],{"class":552},"sTEyZ"," WorkerEntrypoint",[83,555,556],{"class":548}," }",[83,558,559],{"class":544}," from",[83,561,562],{"class":548}," \"",[83,564,566],{"class":565},"sfazB","cloudflare:workers",[83,568,569],{"class":548},"\"",[83,571,572],{"class":548},";\n",[83,574,575],{"class":85,"line":92},[83,576,578],{"emptyLinePlaceholder":577},true,"\n",[83,580,581,584,588,592,595,597,600,603,606],{"class":85,"line":125},[83,582,583],{"class":544},"export",[83,585,587],{"class":586},"spNyl"," class",[83,589,591],{"class":590},"sBMFI"," Plugin",[83,593,594],{"class":586}," extends",[83,596,553],{"class":590},[83,598,599],{"class":548},"\u003C",[83,601,602],{"class":590},"Env",[83,604,605],{"class":548},">",[83,607,608],{"class":548}," {\n",[83,610,612,615,619,622,626,628,631,634,637,639,642,644],{"class":85,"line":611},4,[83,613,614],{"class":586},"    override",[83,616,618],{"class":617},"swJcz"," fetch",[83,620,621],{"class":548},"(",[83,623,625],{"class":624},"sHdIc","request",[83,627,105],{"class":548},[83,629,630],{"class":590}," Request",[83,632,633],{"class":548},"):",[83,635,636],{"class":590}," Promise",[83,638,599],{"class":548},[83,640,641],{"class":590},"Response",[83,643,605],{"class":548},[83,645,608],{"class":548},[83,647,649,652,656,658,660,663,666,669,672],{"class":85,"line":648},5,[83,650,651],{"class":544},"        return",[83,653,655],{"class":654},"s2Zo4"," handle",[83,657,621],{"class":617},[83,659,625],{"class":552},[83,661,662],{"class":548},",",[83,664,665],{"class":548}," this.",[83,667,668],{"class":552},"env",[83,670,671],{"class":617},")",[83,673,572],{"class":548},[83,675,677],{"class":85,"line":676},6,[83,678,679],{"class":548},"    }\n",[83,681,683],{"class":85,"line":682},7,[83,684,685],{"class":548},"}\n",[42,687,688,689,692,693,695],{},"Set ",[46,690,691],{},"\"workers_dev\": false"," in the plugin's ",[46,694,104],{}," and give it no route, so it has no public address at all.",[42,697,698,699,701,702,705,706,709],{},"A plugin meant to be reached over https serves the same two requests from its default ",[46,700,531],{}," handler instead. It gets ",[46,703,704],{},"Authorization: Bearer \u003Ckey>"," and must answer ",[46,707,708],{},"401"," to anything else.",[267,711,713],{"id":712},"post-v1event",[46,714,715],{},"POST \u002Fv1\u002Fevent",[42,717,718],{},"Your server sends this for each message:",[74,720,724],{"className":721,"code":722,"language":723,"meta":79,"style":79},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"event\": \"receive\",\n  \"account\": \"\u003Caccount id>\",\n  \"message\": {\n    \"from\": { \"name\": \"The Dude\", \"email\": \"thedude72@gmail.com\" },\n    \"to\": [\"sam@example.com\"],\n    \"cc\": [],\n    \"subject\": \"The rug\",\n    \"messageId\": \"\u003Cabc@mail.gmail.com>\",\n    \"date\": 1794268800000,\n    \"rcpt\": \"sam@example.com\",\n    \"junk\": false\n  },\n  \"headers\": \"...\",\n  \"body\": \"...\"\n}\n","json",[46,725,726,731,753,773,786,838,861,875,896,917,934,954,969,975,995,1013],{"__ignoreMap":79},[83,727,728],{"class":85,"line":86},[83,729,730],{"class":548},"{\n",[83,732,733,736,739,741,743,745,748,750],{"class":85,"line":92},[83,734,735],{"class":548},"  \"",[83,737,738],{"class":586},"event",[83,740,569],{"class":548},[83,742,105],{"class":548},[83,744,562],{"class":548},[83,746,747],{"class":565},"receive",[83,749,569],{"class":548},[83,751,752],{"class":548},",\n",[83,754,755,757,760,762,764,766,769,771],{"class":85,"line":125},[83,756,735],{"class":548},[83,758,759],{"class":586},"account",[83,761,569],{"class":548},[83,763,105],{"class":548},[83,765,562],{"class":548},[83,767,768],{"class":565},"\u003Caccount id>",[83,770,569],{"class":548},[83,772,752],{"class":548},[83,774,775,777,780,782,784],{"class":85,"line":611},[83,776,735],{"class":548},[83,778,779],{"class":586},"message",[83,781,569],{"class":548},[83,783,105],{"class":548},[83,785,608],{"class":548},[83,787,788,791,794,796,798,800,802,806,808,810,812,815,817,819,821,824,826,828,830,833,835],{"class":85,"line":648},[83,789,790],{"class":548},"    \"",[83,792,793],{"class":590},"from",[83,795,569],{"class":548},[83,797,105],{"class":548},[83,799,549],{"class":548},[83,801,562],{"class":548},[83,803,805],{"class":804},"sbssI","name",[83,807,569],{"class":548},[83,809,105],{"class":548},[83,811,562],{"class":548},[83,813,814],{"class":565},"The Dude",[83,816,569],{"class":548},[83,818,662],{"class":548},[83,820,562],{"class":548},[83,822,823],{"class":804},"email",[83,825,569],{"class":548},[83,827,105],{"class":548},[83,829,562],{"class":548},[83,831,832],{"class":565},"thedude72@gmail.com",[83,834,569],{"class":548},[83,836,837],{"class":548}," },\n",[83,839,840,842,845,847,849,852,854,857,859],{"class":85,"line":676},[83,841,790],{"class":548},[83,843,844],{"class":590},"to",[83,846,569],{"class":548},[83,848,105],{"class":548},[83,850,851],{"class":548}," [",[83,853,569],{"class":548},[83,855,856],{"class":565},"sam@example.com",[83,858,569],{"class":548},[83,860,128],{"class":548},[83,862,863,865,868,870,872],{"class":85,"line":682},[83,864,790],{"class":548},[83,866,867],{"class":590},"cc",[83,869,569],{"class":548},[83,871,105],{"class":548},[83,873,874],{"class":548}," [],\n",[83,876,878,880,883,885,887,889,892,894],{"class":85,"line":877},8,[83,879,790],{"class":548},[83,881,882],{"class":590},"subject",[83,884,569],{"class":548},[83,886,105],{"class":548},[83,888,562],{"class":548},[83,890,891],{"class":565},"The rug",[83,893,569],{"class":548},[83,895,752],{"class":548},[83,897,899,901,904,906,908,910,913,915],{"class":85,"line":898},9,[83,900,790],{"class":548},[83,902,903],{"class":590},"messageId",[83,905,569],{"class":548},[83,907,105],{"class":548},[83,909,562],{"class":548},[83,911,912],{"class":565},"\u003Cabc@mail.gmail.com>",[83,914,569],{"class":548},[83,916,752],{"class":548},[83,918,920,922,925,927,929,932],{"class":85,"line":919},10,[83,921,790],{"class":548},[83,923,924],{"class":590},"date",[83,926,569],{"class":548},[83,928,105],{"class":548},[83,930,931],{"class":804}," 1794268800000",[83,933,752],{"class":548},[83,935,937,939,942,944,946,948,950,952],{"class":85,"line":936},11,[83,938,790],{"class":548},[83,940,941],{"class":590},"rcpt",[83,943,569],{"class":548},[83,945,105],{"class":548},[83,947,562],{"class":548},[83,949,856],{"class":565},[83,951,569],{"class":548},[83,953,752],{"class":548},[83,955,957,959,962,964,966],{"class":85,"line":956},12,[83,958,790],{"class":548},[83,960,961],{"class":590},"junk",[83,963,569],{"class":548},[83,965,105],{"class":548},[83,967,968],{"class":548}," false\n",[83,970,972],{"class":85,"line":971},13,[83,973,974],{"class":548},"  },\n",[83,976,978,980,982,984,986,988,991,993],{"class":85,"line":977},14,[83,979,735],{"class":548},[83,981,434],{"class":586},[83,983,569],{"class":548},[83,985,105],{"class":548},[83,987,562],{"class":548},[83,989,990],{"class":565},"...",[83,992,569],{"class":548},[83,994,752],{"class":548},[83,996,998,1000,1002,1004,1006,1008,1010],{"class":85,"line":997},15,[83,999,735],{"class":548},[83,1001,444],{"class":586},[83,1003,569],{"class":548},[83,1005,105],{"class":548},[83,1007,562],{"class":548},[83,1009,990],{"class":565},[83,1011,1012],{"class":548},"\"\n",[83,1014,1016],{"class":85,"line":1015},16,[83,1017,685],{"class":548},[136,1019,1020,1030],{},[139,1021,1022],{},[142,1023,1024,1027],{},[145,1025,1026],{},"Field",[145,1028,1029],{},"Meaning",[155,1031,1032,1046,1055,1068,1082,1097,1108],{},[142,1033,1034,1038],{},[160,1035,1036],{},[46,1037,738],{},[160,1039,1040,1042,1043],{},[46,1041,747],{}," or ",[46,1044,1045],{},"send",[142,1047,1048,1052],{},[160,1049,1050],{},[46,1051,759],{},[160,1053,1054],{},"The id of the Mailopus account the message belongs to",[142,1056,1057,1062],{},[160,1058,1059],{},[46,1060,1061],{},"message.date",[160,1063,1064,1065],{},"The Date header in milliseconds since 1970, or ",[46,1066,1067],{},"null",[142,1069,1070,1079],{},[160,1071,1072,1075,1076],{},[46,1073,1074],{},"message.rcpt",", ",[46,1077,1078],{},"message.junk",[160,1080,1081],{},"Received mail only: the address it was delivered to, and whether it went to Junk",[142,1083,1084,1088],{},[160,1085,1086],{},[46,1087,434],{},[160,1089,1090,1091,1042,1094,1096],{},"Only with ",[46,1092,1093],{},"--wants headers",[46,1095,444],{},". Unfolded, one header per line.",[142,1098,1099,1103],{},[160,1100,1101],{},[46,1102,444],{},[160,1104,1090,1105,1107],{},[46,1106,456],{},". Plain text, capped at 100,000 characters.",[142,1109,1110,1115],{},[160,1111,1112],{},[46,1113,1114],{},"test",[160,1116,1117,1120,1121],{},[46,1118,1119],{},"true"," when it came from ",[46,1122,1123],{},"mailopus plugin test",[42,1125,1126,1127,1130],{},"A send is one event with every recipient in ",[46,1128,1129],{},"message.to",", Bcc included.",[42,1132,1133,1134,1137,1138,1141],{},"Answer ",[46,1135,1136],{},"2xx"," with a JSON object. A watching plugin can answer ",[46,1139,1140],{},"{}",". A plugin that acts answers with what to do:",[136,1143,1144,1157],{},[139,1145,1146],{},[142,1147,1148,1151,1154],{},[145,1149,1150],{},"Event",[145,1152,1153],{},"Answer",[145,1155,1156],{},"Effect",[155,1158,1159,1173,1187],{},[142,1160,1161,1165,1170],{},[160,1162,1163],{},[46,1164,747],{},[160,1166,1167],{},[46,1168,1169],{},"{\"junk\": true}",[160,1171,1172],{},"Files the message in Junk",[142,1174,1175,1179,1184],{},[160,1176,1177],{},[46,1178,747],{},[160,1180,1181],{},[46,1182,1183],{},"{\"keywords\": [\"$seen\"]}",[160,1185,1186],{},"Sets up to 8 keywords on the message",[142,1188,1189,1193,1198],{},[160,1190,1191],{},[46,1192,1045],{},[160,1194,1195],{},[46,1196,1197],{},"{\"block\": \"\u003Creason>\"}",[160,1199,1200],{},"Stops the send and shows the reason",[42,1202,1203,1204,1206],{},"Anything else in the answer is ignored, so a plugin may add fields of its own. ",[46,1205,372],{}," prints them.",[267,1208,1210],{"id":1209},"post-v1command",[46,1211,1212],{},"POST \u002Fv1\u002Fcommand",[42,1214,1215,1218],{},[46,1216,1217],{},"mailopus with \u003Cplugin> \u003Cwords>"," sends the words as typed:",[74,1220,1222],{"className":721,"code":1221,"language":723,"meta":79,"style":79},"{ \"args\": [\"once\", \"receive\", \"--from=thedude72\", \"slack\"] }\n",[46,1223,1224],{"__ignoreMap":79},[83,1225,1226,1229,1231,1234,1236,1238,1240,1242,1245,1247,1249,1251,1253,1255,1257,1259,1262,1264,1266,1268,1271,1273,1276],{"class":85,"line":86},[83,1227,1228],{"class":548},"{",[83,1230,562],{"class":548},[83,1232,1233],{"class":586},"args",[83,1235,569],{"class":548},[83,1237,105],{"class":548},[83,1239,851],{"class":548},[83,1241,569],{"class":548},[83,1243,1244],{"class":565},"once",[83,1246,569],{"class":548},[83,1248,662],{"class":548},[83,1250,562],{"class":548},[83,1252,747],{"class":565},[83,1254,569],{"class":548},[83,1256,662],{"class":548},[83,1258,562],{"class":548},[83,1260,1261],{"class":565},"--from=thedude72",[83,1263,569],{"class":548},[83,1265,662],{"class":548},[83,1267,562],{"class":548},[83,1269,1270],{"class":565},"slack",[83,1272,569],{"class":548},[83,1274,1275],{"class":548},"]",[83,1277,1278],{"class":548}," }\n",[42,1280,1281],{},"Answer with JSON:",[136,1283,1284,1293],{},[139,1285,1286],{},[142,1287,1288,1290],{},[145,1289,1026],{},[145,1291,1292],{},"Effect on the command line",[155,1294,1295,1305,1318],{},[142,1296,1297,1302],{},[160,1298,1299],{},[46,1300,1301],{},"text",[160,1303,1304],{},"Printed",[142,1306,1307,1312],{},[160,1308,1309],{},[46,1310,1311],{},"sound",[160,1313,1314,1315,1317],{},"Plays a sound on the machine running the command. ",[46,1316,1119],{}," plays a default one, and a string is a file path on that machine.",[142,1319,1320,1325],{},[160,1321,1322],{},[46,1323,1324],{},"again",[160,1326,1327,1328,1331],{},"Runs the same command again after this many seconds. This is how a command such as gotmail's ",[46,1329,1330],{},"listen"," keeps waiting.",[42,1333,1334,1335,1337,1338,1340,1341,1344],{},"A status other than ",[46,1336,1136],{}," prints ",[46,1339,1301],{}," and makes the command fail, so use ",[46,1342,1343],{},"400"," for a command the plugin can't parse.",[267,1346,1348],{"id":1347},"start-from-the-example","Start from the example",[42,1350,1351,1352,1357,1358,1361,1362,1365],{},"Copy ",[53,1353,1355],{"href":55,"rel":1354},[57],[46,1356,60],{},". Its ",[46,1359,1360],{},"src\u002Findex.ts"," has the bound entrypoint and the keyed public handler, ",[46,1363,1364],{},"src\u002Fapp.ts"," has both endpoints, and its tests run the whole plugin without a network. A plugin keeps its own data in its own storage. Your Mailopus database is not shared with it.",[1367,1368,1369],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":79,"searchDepth":92,"depth":92,"links":1371},[1372,1375,1376,1379,1380,1381],{"id":65,"depth":92,"text":66,"children":1373},[1374],{"id":269,"depth":125,"text":270},{"id":291,"depth":92,"text":292},{"id":329,"depth":92,"text":330,"children":1377},[1378],{"id":362,"depth":125,"text":363},{"id":402,"depth":92,"text":221},{"id":459,"depth":92,"text":238},{"id":512,"depth":92,"text":513,"children":1382},[1383,1384,1385],{"id":712,"depth":125,"text":715},{"id":1209,"depth":125,"text":1212},{"id":1347,"depth":125,"text":1348},"md",null,{},{"title":25},{"description":1391,"title":25},"A plugin is a Worker you deploy that your server tells about mail as it arrives and as it goes out. A plugin can watch, and if you let it, it can file received mail or stop a send. You run a plugin's own commands through pnpm mailopus with \u003Cplugin> ....","_G19YeT6dBFR_xsYQzqxpjmOOKyDc98ui-zpy8UN5zc",[1394,1395],{"title":21,"path":22,"stem":23,"description":79,"children":-1},{"title":29,"path":30,"stem":31,"description":79,"children":-1},1794297747172]