Za koordinatore · Glasaj iz Amerike i Kanade · 2. oktobar 2026.

Poslednji dan za prijavu. Šaljemo svi, danas.

Broadcast lista nam ne radi. Zato predlažemo da svaki koordinator pošalje mejl svojim kontaktima sa svog Gmail naloga, svakome posebno, i da to za njega uradi Claude. Ispod su tekst mejla, WhatsApp poruke i uputstvo korak po korak.

Zvanični rok: 3. oktobar u 18:00 ET / 15:00 PT (ponoć u Srbiji) U komunikaciji: „danas je poslednji dan“

Dijaspora se prijavljuje kao nikad pre

73.000+
prijavljenih za glasanje u inostranstvu, do 1. oktobra
~40.000
na prošlim izborima: skoro duplo manje
~130
očekivanih biračkih mesta u svetu (prošli put 80)
2.300+
zahteva preko glasaj-usa-canada.org, u 11 gradova SAD i Kanade

Ovo je neverovatan rezultat, i on je naš: svaki koordinator, svaka poruka, svaki prosleđen link. Danas je poslednji dan da ga povećamo. Biračko mesto se otvara gde ima bar 100 prijavljenih, pa svaka prijava u gradu koji je blizu te granice može da odluči da li će ljudi glasati tamo ili putovati stotinama kilometara.

Izvori: Dušan Dinčić, član RIK-a, i Ognjen Rašuo, član Komisije za reviziju biračkog spiska, za Nova.rs, 1. 10. 2026. To su izjave, ne zvanično objavljeni podaci RIK-a. Broj zahteva za SAD i Kanadu je sa glasaj-usa-canada.org.

Šta tražimo od ljudi

Većina naših kontakata je već predala zahtev. Zato mejl ne traži prvo da se prijave, već tri stvari:

Šta je Claude Code i po čemu je drugačiji

Običan Claude (chat)

Razgovaraš sa njim na claude.ai ili u aplikaciji. Piše tekstove i odgovara na pitanja, ali ne vidi fajlove na tvom računaru i ne može da pokrene ništa na njemu. Mejlove bi morao/la da šalješ sam/a, jedan po jedan.

Claude Code

Isti Claude, ali radi na tvom računaru, u folderu koji mu izabereš. Može da čita fajlove u tom folderu (na primer tvoj CSV), da pravi nove i da pokreće programe (na primer skript koji šalje mejlove). Pre svake komande te pita za dozvolu, pa uvek vidiš šta radi.

Claude Code je deo aplikacije Claude za Mac ili Windows (claude.ai/download), ikonica </> na vrhu. Traži plaćeni nalog (Pro ili veći). Ako klik na </> traži nadogradnju, nalog je besplatan. Tada pošalji ručno ili zamoli drugog koordinatora da pošalje umesto tebe.

1. Prebaci se na „Code“. Instaliraj aplikaciju Claude i prijavi se. Na vrhu je prekidač sa dve ikonice: oblačić je običan chat, a </> je Claude Code. Klikni </>.
2. Izaberi „Local“ i folder. „Local“ znači tvoj računar, ne „Cloud“, jer Claude u oblaku ne vidi tvoje fajlove i ne može da šalje preko Gmaila. Klikni „Select folder“ i izaberi folder gotv. Režim neka ostane „Manual“: Claude te pita pre svake komande.
3. Nalepi poruku i odobravaj komande. Claude napravi skript i pre pokretanja traži dozvolu. Pročitaj komandu i klikni dozvolu (u aplikaciji dugme piše „Allow“ ili slično). Ako ti nešto nije jasno, samo ga pitaj.
4. Proba, pa „šalji“. Claude ti pokaže koliko ima kontakata i primer mejla, pošalje probu samo tebi, a pravo slanje kreće tek kad napišeš „šalji“. Dok šalje, javlja ti napredak.

Ilustracije su pojednostavljene. Prava aplikacija izgleda malo drugačije i dugmad mogu da budu na engleskom, ali su koraci isti.

Kako da pošalješ: Claude + Gmail app password

Oko 15 minuta pripreme, a onda Claude šalje sam. Svaki kontakt dobija poseban mejl, u kome je samo njegova adresa u polju „To“, i pozdrav sa njegovim imenom. Bez BCC-a, jer takvi mejlovi često završe u spamu.

Treba ti Claude Code na tvom računaru: aplikacija Claude za Mac ili Windows, ikonica </> na vrhu, ili claude u terminalu. Obični chat na claude.ai i Claude u oblaku ne mogu da šalju mejl preko Gmaila.
  1. 1

    Napravi folder i stavi u njega kontakte

    Na primer folder gotv na desktopu. U njega sačuvaj svoje kontakte kao CSV (u Google Sheets: File → Download → Comma-separated values). Dovoljna je kolona sa mejlom, a dobro je da postoje i ime i drzava ili grad, da Kanađani dobiju kanadsku verziju. Claude ume da sredi i drugačije kolone.

    Podelite liste između koordinatora, da niko ne dobije mejl dva puta.

  2. 2

    Napravi Gmail app password

    Lozinku ne lepi u Claude i ne šalji je nikome. Skript je sam čita iz fajla. Posle slanja je obriši na istoj stranici (dugme sa kantom).

    Ako stranica kaže da app password nije dostupan, nalog je verovatno Workspace (firma ili fakultet) i administrator ga je isključio. Koristi lični Gmail.

  3. 3

    Otvori Claude Code u tom folderu i nalepi ovu poruku

    Poruka sadrži i ceo skript za slanje (kratak je), pa ništa ne moraš da preuzimaš. Pre lepljenja zameni tri stvari u uglastim zagradama na početku poruke.

    Moji podaci: Gmail adresa [moja-adresa@gmail.com], ime za potpis [Moje Ime], fajl sa kontaktima [ime-fajla].csv
    
    Pomozi mi da pošaljem GOTV mejl svojim kontaktima, svakome posebno, sa mog Gmail naloga.
    
    1. Napravi u ovom folderu fajl posalji.py sa tačno onim sadržajem koji je ispod linije ===== SKRIPT =====.
       Ne menjaj ništa u njemu, ni tekst mejla.
    2. App password je u fajlu lozinka.txt. NEMOJ da otvaraš, čitaš, ispisuješ ni kopiraš lozinka.txt.
       Skript ga čita sam.
    3. Pogledaj samo nazive kolona i broj redova u mom CSV-u, ne ispisuj adrese. Ako kolone nisu
       prepoznatljive (email, ime, drzava ili grad), napravi kontakti_gotv.csv sa kolonama
       email,ime,drzava (drzava = "sad" ili "kanada").
    4. Pokreni: python3 posalji.py [csv] --od [moja adresa] --potpis "[moje ime]" --suvo
       i pokaži mi brojke i primer mejla.
    5. Pokreni isto sa --proba umesto --suvo. To šalje dva probna mejla samo meni.
       Sačekaj da ti potvrdim da su stigli u inbox i da izgledaju dobro.
    6. Kad napišem „šalji“, pokreni sa --posalji i pošalji sve, danas. Pokreni ga u pozadini i javljaj mi
       napredak. Gmail dozvoljava oko 500 mejlova dnevno po nalogu, pa skript sam staje na 480. Ako imam
       više kontakata, reci mi koliko je ostalo i pomozi mi da ostatak pošaljem sa drugog Gmail naloga
       (druga --od adresa i njena lozinka u lozinka.txt; poslato.csv preskače već poslate).
       Ako Gmail prijavi grešku, stani i javi mi.
    
    ===== SKRIPT =====
    #!/usr/bin/env python3
    """
    GOTV mejl, 2. oktobar 2026: šalje svakom kontaktu poseban mejl sa tvog Gmail naloga.
    
    Bez BCC-a: svaki primalac dobija svoj mejl, samo njegova adresa je u „To“.
    Radi samo sa Python standardnom bibliotekom (ništa ne treba instalirati).
    
    Lozinka (Gmail „app password“, 16 slova) se čita, tim redom, iz:
      1. promenljive GMAIL_APP_PASSWORD
      2. fajla lozinka.txt u istom folderu (napravi ga sam/a; Claude ga ne treba otvarati)
      3. pitanja u terminalu (ako skript pokrećeš sam/a)
    
    Primeri:
      python3 posalji.py kontakti.csv --od ti@gmail.com --potpis "Marko" --suvo
      python3 posalji.py kontakti.csv --od ti@gmail.com --potpis "Marko" --proba
      python3 posalji.py kontakti.csv --od ti@gmail.com --potpis "Marko" --posalji
    
    kontakti.csv: prvi red su nazivi kolona. Prepoznaje: email/mejl/e-mail, ime/name/first name,
    drzava/zemlja/country, grad/city. Kanada se prepoznaje po državi ili po gradu (Toronto, Vankuver,
    Otava, Kalgari...). Sve ostalo dobija verziju za SAD, osim ako zadaš --zemlja.
    Poslati se upisuju u poslato.csv, pa ponovno pokretanje preskače one kojima je već poslato.
    """
    
    import argparse
    import csv
    import getpass
    import html
    import os
    import re
    import smtplib
    import ssl
    import sys
    import time
    from datetime import datetime
    from email.message import EmailMessage
    from email.utils import formataddr, make_msgid, formatdate
    
    NASLOV = "Danas je poslednji dan: ko još nije prijavljen?"
    
    KONZULATI = {
        "sad": [
            "- Njujork: izbori.njujork@mfa.rs",
            "- Vašington i Majami: izbori@serbiaembusa.org",
            "- Čikago, Hjuston, Los Anđeles, San Francisko, San Dijego: izbori.cikago2026@mfa.rs",
        ],
        "kanada": [
            "- Toronto: izbori@rogers.com",
            "- Otava, Vankuver i Kalgari: consular.ottawa@mfa.rs",
        ],
    }
    
    ROK = {
        "sad": "Zvanični rok je sutra u 6 popodne po njujorškom vremenu (3 popodne u Kaliforniji).",
        "kanada": "Zvanični rok je sutra u 6 popodne po torontskom vremenu (4 popodne u Kalgariju, 3 popodne u Vankuveru).",
    }
    
    # **tekst** = podebljano u HTML verziji. Prazan red = novi pasus.
    TELO = """{pozdrav}
    
    **Danas je poslednji dan za prijavu za glasanje na izborima u Srbiji.** Ako si se već prijavio/la, hvala ti! Do juče se prijavilo više od 73.000 ljudi iz dijaspore, skoro duplo više nego na prošlim izborima (oko 40.000), prema rečima člana RIK-a. Danas nam trebaš za još dve stvari.
    
    **1. Seti se 2–3 prijatelja ili rođaka u dijaspori koji se možda još nisu prijavili, i prosledi im ovaj mejl ili poruku sa dna mejla.** Bilo gde u svetu: Nemačka, Austrija, Švajcarska, Kanada, Amerika… Skoro svako zna nekoga ko „misli da se prijavi“. Ako kažu „sutra ću“, javi im se ponovo večeras:
    - ko glasa u SAD ili Kanadi: https://glasaj-usa-canada.org/zahtev
    - ko glasa u bilo kojoj drugoj zemlji: https://formular.skupstinaurasejanju.org/
    
    Formular za 15 minuta napravi gotov zahtev u PDF-u, koji se pošalje mejlom ambasadi ili konzulatu. Treba samo pasoš ili lična karta i telefon.
    
    **2. Proveri da li je tvoj zahtev stigao: https://upit.birackispisak.gov.rs/**
    Ukucaj JMBG i broj lične karte. Ako piše „glasa u inostranstvu“, sve je u redu.
    
    Ako to ne piše, a zahtev si poslao/la pre više od nekoliko dana, ili nikad nisi dobio/la potvrdu, **piši konzulatu još danas**. Prosledi im svoj prvi mejl (da se vidi kad je poslat) i traži potvrdu prijema:
    {konzulati}
    
    Ovo nije sitnica. Na prošlim izborima ambasade i konzulati su „gubili“ zahteve poslate na vreme, uz objašnjenja poput „pao nam je server“. Ako ti ne odgovore, odgovori na ovaj mejl i borićemo se za tebe.
    
    **Zašto danas, a ne sutra?** {rok} Ali poslednjeg dana se „izgubi“ najviše zahteva. Borićemo se i za one koji pošalju sutra, ali bolje da ne rizikuju.
    
    A ako se ni ti još nisi prijavio/la: https://glasaj-usa-canada.org/zahtev, danas.
    
    Srbija ne prestaje na granici.
    
    Pozdrav,
    {potpis}
    Glasaj iz Amerike i Kanade
    https://glasaj-usa-canada.org
    
    ---
    
    **Poruka za prosleđivanje (kopiraj u WhatsApp, Viber ili SMS):**
    
    Danas je poslednji dan da se prijaviš za glasanje na izborima u Srbiji! Traje 15 minuta, treba ti samo pasoš ili lična karta i telefon.
    Ako glasaš u SAD ili Kanadi: glasaj-usa-canada.org/zahtev
    Ako glasaš bilo gde drugde: formular.skupstinaurasejanju.org
    Formular napravi gotov PDF, ti ga samo pošalješ mejlom ambasadi ili konzulatu. Uradi to danas, ne sutra.
    
    ---
    
    Dobijaš ovaj mejl jer si u kontaktu sa koordinatorima Glasaj iz Amerike i Kanade. Ako ne želiš više ovakve poruke, odgovori „ne“."""
    
    KANADA_GRADOVI = ("toronto", "vankuver", "vancouver", "otava", "ottawa", "kalgari", "calgary",
                      "montreal", "monreal", "edmonton", "mississauga", "misisaga", "hamilton", "winnipeg",
                      "vinipeg", "kitchener", "london, on", "surrey", "burnaby", "halifax", "victoria")
    
    
    def tekst(zemlja, ime="", potpis="[Ime]"):
        ime = (ime or "").strip()
        return TELO.format(
            pozdrav=f"Zdravo {ime}," if ime else "Zdravo,",
            konzulati="\n".join(KONZULATI[zemlja]),
            rok=ROK[zemlja],
            potpis=potpis,
        )
    
    
    def u_html(t):
        def red(s):
            s = html.escape(s, quote=False)
            s = re.sub(r"\*\*(.+?)\*\*", r"<b>\1</b>", s)
            s = re.sub(r"(https?://[^\s<,]+[^\s<,.])", r'<a href="\1">\1</a>', s)
            s = re.sub(r"\b([\w.+-]+@[\w-]+\.[\w.-]*\w)\b", r'<a href="mailto:\1">\1</a>', s)
            return s
        delovi = []
        for pasus in t.split("\n\n"):
            if pasus.strip() == "---":
                delovi.append('<hr style="border:none;border-top:1px solid #ddd;margin:20px 0">')
                continue
            delovi.append('<p style="margin:0 0 14px">' + "<br>".join(red(r) for r in pasus.split("\n")) + "</p>")
        return ('<div style="font-family:Arial,Helvetica,sans-serif;font-size:15px;line-height:1.5;color:#16181d;max-width:600px">'
                + "".join(delovi) + "</div>")
    
    
    def u_tekst(t):
        return t.replace("**", "")
    
    
    def kolona(red, *imena):
        for k, v in red.items():
            if k and k.strip().lower() in imena:
                return (v or "").strip()
        return ""
    
    
    def zemlja_za(red, podrazumevana):
        d = kolona(red, "drzava", "država", "zemlja", "country").lower()
        if d:
            if d in ("ca", "kan") or d.startswith(("kanad", "canad")):
                return "kanada"
            return "sad"
        g = kolona(red, "grad", "city", "mesto").lower()
        if g and any(x in g for x in KANADA_GRADOVI):
            return "kanada"
        return podrazumevana
    
    
    def ucitaj(putanja, podrazumevana):
        with open(putanja, newline="", encoding="utf-8-sig") as f:
            redovi = list(csv.DictReader(f))
        vidjeni, kontakti, lose = set(), [], 0
        for r in redovi:
            mejl = kolona(r, "email", "e-mail", "mejl", "mail", "email address", "adresa").lower()
            if not re.fullmatch(r"[^@\s,;]+@[^@\s,;]+\.[a-z]{2,}", mejl):
                lose += 1
                continue
            if mejl in vidjeni:
                continue
            vidjeni.add(mejl)
            ime = kolona(r, "ime", "name", "first name", "first_name", "firstname")
            ime = ime.split()[0] if ime else ""
            kontakti.append((mejl, ime, zemlja_za(r, podrazumevana)))
        return kontakti, lose
    
    
    def vec_poslato(log):
        if not os.path.exists(log):
            return set()
        with open(log, newline="", encoding="utf-8") as f:
            return {r["email"] for r in csv.DictReader(f) if r.get("status") == "poslato"}
    
    
    def upisi(log, mejl, status, napomena=""):
        novi = not os.path.exists(log)
        with open(log, "a", newline="", encoding="utf-8") as f:
            w = csv.writer(f)
            if novi:
                w.writerow(["email", "status", "vreme", "napomena"])
            w.writerow([mejl, status, datetime.now().isoformat(timespec="seconds"), napomena])
    
    
    def lozinka(folder):
        p = os.environ.get("GMAIL_APP_PASSWORD", "").strip()
        if not p:
            f = os.path.join(folder, "lozinka.txt")
            if os.path.exists(f):
                p = open(f, encoding="utf-8").read().strip()
        if not p and sys.stdin.isatty():
            p = getpass.getpass("Gmail app password (16 slova, ne prikazuje se): ").strip()
        if not p:
            sys.exit("Nema lozinke. Napravi lozinka.txt u ovom folderu (samo lozinka, jedan red) ili postavi GMAIL_APP_PASSWORD.")
        return p.replace(" ", "")
    
    
    def poruka(od, potpis, kome, ime, zemlja):
        t = tekst(zemlja, ime, potpis)
        m = EmailMessage()
        m["From"] = formataddr((potpis, od))
        m["To"] = kome
        m["Subject"] = NASLOV
        m["Date"] = formatdate(localtime=True)
        m["Message-ID"] = make_msgid(domain=od.split("@")[1])
        m.set_content(u_tekst(t))
        m.add_alternative(u_html(t), subtype="html")
        return m
    
    
    def konekcija(od, loz):
        s = smtplib.SMTP("smtp.gmail.com", 587, timeout=30)
        s.starttls(context=ssl.create_default_context())
        s.login(od, loz)
        return s
    
    
    def main():
        a = argparse.ArgumentParser(description="GOTV mejl: pojedinačno slanje sa Gmail naloga")
        a.add_argument("csv", help="fajl sa kontaktima (CSV)")
        a.add_argument("--od", required=True, help="tvoja Gmail adresa")
        a.add_argument("--potpis", required=True, help="ime u potpisu i u polju From")
        a.add_argument("--zemlja", choices=["sad", "kanada"], help="ista verzija za sve (inače po koloni drzava/grad, a podrazumevano SAD)")
        g = a.add_mutually_exclusive_group(required=True)
        g.add_argument("--suvo", action="store_true", help="ništa ne šalje: prikaže brojke i prvi mejl")
        g.add_argument("--proba", action="store_true", help="pošalje po jedan probni mejl (SAD i Kanada) samo tebi")
        g.add_argument("--posalji", action="store_true", help="pravo slanje")
        a.add_argument("--limit", type=int, default=480, help="najviše mejlova sa ovog naloga danas (podrazumevano 480; Gmail dozvoljava oko 500 dnevno)")
        a.add_argument("--pauza", type=float, default=2, help="sekundi između mejlova (podrazumevano 2)")
        a.add_argument("--log", default="poslato.csv", help="dnevnik poslatih (podrazumevano poslato.csv)")
        x = a.parse_args()
    
        kontakti, lose = ucitaj(x.csv, x.zemlja or "sad")
        if x.zemlja:
            kontakti = [(m, i, x.zemlja) for m, i, _ in kontakti]
        gotovi = vec_poslato(x.log)
        preostali = [k for k in kontakti if k[0] not in gotovi]
        sad = sum(1 for k in preostali if k[2] == "sad")
        print(f"Kontakata: {len(kontakti)} (neispravnih adresa preskočeno: {lose}, već poslato: {len(kontakti) - len(preostali)})")
        print(f"Za slanje: {len(preostali)}  ·  SAD: {sad}  ·  Kanada: {len(preostali) - sad}  ·  sa ovog naloga najviše {x.limit}")
        if len(preostali) > x.limit:
            print(f"PAŽNJA: {len(preostali) - x.limit} kontakata preko Gmail dnevnog limita. Daj ih drugom nalogu (isti CSV + isti poslato.csv preskače poslate).")
    
        if x.suvo:
            if preostali:
                m, i, z = preostali[0]
                print(f"\n--- Primer: {m} ({z}) ---\nNaslov: {NASLOV}\n")
                print(u_tekst(tekst(z, i, x.potpis)))
            print("\nNišta nije poslato (--suvo).")
            return
    
        loz = lozinka(os.path.dirname(os.path.abspath(__file__)))
        try:
            s = konekcija(x.od, loz)
        except smtplib.SMTPAuthenticationError:
            sys.exit("Gmail nije prihvatio prijavu. Proveri adresu i app password (ne običnu lozinku) i da je uključena dvostepena provera.")
    
        if x.proba:
            for z in ("sad", "kanada"):
                s.send_message(poruka(x.od, x.potpis, x.od, "Probni", z))
                print(f"Probni mejl ({z}) poslat na {x.od}. Proveri inbox (i spam) i kako izgleda na telefonu.")
            s.quit()
            return
    
        poslato = 0
        for n, (mejl, ime, z) in enumerate(preostali[: x.limit], 1):
            for pokusaj in (1, 2):
                try:
                    s.send_message(poruka(x.od, x.potpis, mejl, ime, z))
                    upisi(x.log, mejl, "poslato", z)
                    poslato += 1
                    print(f"[{n}/{min(x.limit, len(preostali))}] {mejl} ({z})")
                    break
                except smtplib.SMTPRecipientsRefused as e:
                    upisi(x.log, mejl, "odbijeno", str(e)[:200])
                    print(f"[{n}] {mejl}: adresa odbijena, preskačem")
                    break
                except smtplib.SMTPServerDisconnected:
                    if pokusaj == 2:
                        raise
                    time.sleep(5)
                    s = konekcija(x.od, loz)
                except (smtplib.SMTPDataError, smtplib.SMTPSenderRefused) as e:
                    upisi(x.log, mejl, "greska", str(e)[:200])
                    print(f"\nGmail je odbio slanje: {e}\nVerovatno je dostignut dnevni limit ili je nalog privremeno blokiran. "
                          f"Stajem. Poslato u ovom pokretanju: {poslato}. Nastavi kasnije istom komandom (preskače poslate).")
                    s.quit()
                    return
            time.sleep(x.pauza)
        s.quit()
        ostalo = len(preostali) - poslato
        print(f"\nGotovo. Poslato: {poslato}. Preostalo: {ostalo}." + (" Ostatak pošalji sa drugog Gmail naloga (ista komanda, druga --od adresa i lozinka)." if ostalo else ""))
    
    
    if __name__ == "__main__":
        main()
    
  4. 4

    Proveri probni mejl i reci „šalji“

    Pogledaj probni mejl na telefonu: da li je u inboxu (ne u spamu), da li linkovi rade i da li je potpis dobar. Onda napiši Claude-u „šalji“. Šalje sve odjednom, oko 500 mejlova za 20–30 minuta.

  5. 5

    Prati odgovore i obriši app password

    Mejl obećava „odgovori na ovaj mejl i borićemo se za tebe“, pa danas i sutra neko mora da odgovara. Kad završiš sa slanjem, obriši app password na myaccount.google.com/apppasswords i fajl lozinka.txt.

Pošalji sve danas

Tekst mejla

Naslov: Danas je poslednji dan: ko još nije prijavljen? · Pozdrav dobija ime kontakta („Zdravo Ana,“), a ako imena nema, samo „Zdravo,“. Skript sam bira verziju za SAD ili za Kanadu.

Zdravo Ana,

Danas je poslednji dan za prijavu za glasanje na izborima u Srbiji. Ako si se već prijavio/la, hvala ti! Do juče se prijavilo više od 73.000 ljudi iz dijaspore, skoro duplo više nego na prošlim izborima (oko 40.000), prema rečima člana RIK-a. Danas nam trebaš za još dve stvari.

1. Seti se 2–3 prijatelja ili rođaka u dijaspori koji se možda još nisu prijavili, i prosledi im ovaj mejl ili poruku sa dna mejla. Bilo gde u svetu: Nemačka, Austrija, Švajcarska, Kanada, Amerika… Skoro svako zna nekoga ko „misli da se prijavi“. Ako kažu „sutra ću“, javi im se ponovo večeras:
- ko glasa u SAD ili Kanadi: https://glasaj-usa-canada.org/zahtev
- ko glasa u bilo kojoj drugoj zemlji: https://formular.skupstinaurasejanju.org/

Formular za 15 minuta napravi gotov zahtev u PDF-u, koji se pošalje mejlom ambasadi ili konzulatu. Treba samo pasoš ili lična karta i telefon.

2. Proveri da li je tvoj zahtev stigao: https://upit.birackispisak.gov.rs/
Ukucaj JMBG i broj lične karte. Ako piše „glasa u inostranstvu“, sve je u redu.

Ako to ne piše, a zahtev si poslao/la pre više od nekoliko dana, ili nikad nisi dobio/la potvrdu, piši konzulatu još danas. Prosledi im svoj prvi mejl (da se vidi kad je poslat) i traži potvrdu prijema:
- Njujork: izbori.njujork@mfa.rs
- Vašington i Majami: izbori@serbiaembusa.org
- Čikago, Hjuston, Los Anđeles, San Francisko, San Dijego: izbori.cikago2026@mfa.rs

Ovo nije sitnica. Na prošlim izborima ambasade i konzulati su „gubili“ zahteve poslate na vreme, uz objašnjenja poput „pao nam je server“. Ako ti ne odgovore, odgovori na ovaj mejl i borićemo se za tebe.

Zašto danas, a ne sutra? Zvanični rok je sutra u 6 popodne po njujorškom vremenu (3 popodne u Kaliforniji). Ali poslednjeg dana se „izgubi“ najviše zahteva. Borićemo se i za one koji pošalju sutra, ali bolje da ne rizikuju.

A ako se ni ti još nisi prijavio/la: https://glasaj-usa-canada.org/zahtev, danas.

Srbija ne prestaje na granici.

Pozdrav,
[tvoje ime]
Glasaj iz Amerike i Kanade
https://glasaj-usa-canada.org


Poruka za prosleđivanje (kopiraj u WhatsApp, Viber ili SMS):

Danas je poslednji dan da se prijaviš za glasanje na izborima u Srbiji! Traje 15 minuta, treba ti samo pasoš ili lična karta i telefon.
Ako glasaš u SAD ili Kanadi: glasaj-usa-canada.org/zahtev
Ako glasaš bilo gde drugde: formular.skupstinaurasejanju.org
Formular napravi gotov PDF, ti ga samo pošalješ mejlom ambasadi ili konzulatu. Uradi to danas, ne sutra.


Dobijaš ovaj mejl jer si u kontaktu sa koordinatorima Glasaj iz Amerike i Kanade. Ako ne želiš više ovakve poruke, odgovori „ne“.

Običan tekst za kopiranje (ako šalješ ručno)
Zdravo,

Danas je poslednji dan za prijavu za glasanje na izborima u Srbiji. Ako si se već prijavio/la, hvala ti! Do juče se prijavilo više od 73.000 ljudi iz dijaspore, skoro duplo više nego na prošlim izborima (oko 40.000), prema rečima člana RIK-a. Danas nam trebaš za još dve stvari.

1. Seti se 2–3 prijatelja ili rođaka u dijaspori koji se možda još nisu prijavili, i prosledi im ovaj mejl ili poruku sa dna mejla. Bilo gde u svetu: Nemačka, Austrija, Švajcarska, Kanada, Amerika… Skoro svako zna nekoga ko „misli da se prijavi“. Ako kažu „sutra ću“, javi im se ponovo večeras:
- ko glasa u SAD ili Kanadi: https://glasaj-usa-canada.org/zahtev
- ko glasa u bilo kojoj drugoj zemlji: https://formular.skupstinaurasejanju.org/

Formular za 15 minuta napravi gotov zahtev u PDF-u, koji se pošalje mejlom ambasadi ili konzulatu. Treba samo pasoš ili lična karta i telefon.

2. Proveri da li je tvoj zahtev stigao: https://upit.birackispisak.gov.rs/
Ukucaj JMBG i broj lične karte. Ako piše „glasa u inostranstvu“, sve je u redu.

Ako to ne piše, a zahtev si poslao/la pre više od nekoliko dana, ili nikad nisi dobio/la potvrdu, piši konzulatu još danas. Prosledi im svoj prvi mejl (da se vidi kad je poslat) i traži potvrdu prijema:
- Njujork: izbori.njujork@mfa.rs
- Vašington i Majami: izbori@serbiaembusa.org
- Čikago, Hjuston, Los Anđeles, San Francisko, San Dijego: izbori.cikago2026@mfa.rs

Ovo nije sitnica. Na prošlim izborima ambasade i konzulati su „gubili“ zahteve poslate na vreme, uz objašnjenja poput „pao nam je server“. Ako ti ne odgovore, odgovori na ovaj mejl i borićemo se za tebe.

Zašto danas, a ne sutra? Zvanični rok je sutra u 6 popodne po njujorškom vremenu (3 popodne u Kaliforniji). Ali poslednjeg dana se „izgubi“ najviše zahteva. Borićemo se i za one koji pošalju sutra, ali bolje da ne rizikuju.

A ako se ni ti još nisi prijavio/la: https://glasaj-usa-canada.org/zahtev, danas.

Srbija ne prestaje na granici.

Pozdrav,
[tvoje ime]
Glasaj iz Amerike i Kanade
https://glasaj-usa-canada.org

---

Poruka za prosleđivanje (kopiraj u WhatsApp, Viber ili SMS):

Danas je poslednji dan da se prijaviš za glasanje na izborima u Srbiji! Traje 15 minuta, treba ti samo pasoš ili lična karta i telefon.
Ako glasaš u SAD ili Kanadi: glasaj-usa-canada.org/zahtev
Ako glasaš bilo gde drugde: formular.skupstinaurasejanju.org
Formular napravi gotov PDF, ti ga samo pošalješ mejlom ambasadi ili konzulatu. Uradi to danas, ne sutra.

---

Dobijaš ovaj mejl jer si u kontaktu sa koordinatorima Glasaj iz Amerike i Kanade. Ako ne želiš više ovakve poruke, odgovori „ne“.

WhatsApp i Viber

Pošalji dve poruke jednu za drugom. Prva je molba, a drugu ljudi prosleđuju jednim dodirom („Forward“).

1. Molba

Zdravo! Ovde tim *Glasaj iz Amerike i Kanade*.

Hvala ti što si se prijavio/la za glasanje. *Danas je poslednji dan za prijavu*, i imamo jednu veliku molbu:

*Seti se 2–3 prijatelja ili rođaka u dijaspori koji se možda još nisu prijavili, i prosledi im poruku ispod.*

Bilo gde u svetu: Nemačka, Austrija, Švajcarska, Kanada, Amerika… Skoro svako zna nekoga ko „misli da se prijavi“. Jedna tvoja poruka može da bude razlika između toga da glasa ili ne.

Ako kažu „sutra ću“, javi im se ponovo večeras.

Hvala ti! 🙏

_P.S. Ako nisi dobio/la potvrdu za svoj zahtev, proveri na upit.birackispisak.gov.rs. Ako ne piše „glasa u inostranstvu“, javi nam se ovde._

2. Za prosleđivanje

*Danas je poslednji dan da se prijaviš za glasanje na izborima u Srbiji!*

Traje 15 minuta, treba ti samo pasoš ili lična karta i telefon.

👉 Ako glasaš u SAD ili Kanadi: glasaj-usa-canada.org/zahtev
👉 Ako glasaš bilo gde drugde: formular.skupstinaurasejanju.org

Formular napravi gotov PDF, ti ga samo pošalješ mejlom ambasadi ili konzulatu. *Uradi to danas, ne sutra.*

Pitanja

Da li je bezbedno da Claude šalje sa mog naloga?

App password daje pristup tvom mejlu, zato ga drži samo u lozinka.txt na svom računaru, ne lepi ga u chat i obriši ga posle slanja. Skript (možeš da ga pročitaš, kratak je) koristi lozinku samo za slanje preko smtp.gmail.com. Ne čita tvoje mejlove i ne šalje podatke nikome drugom. Claude pokreće skript i vidi brojke, a od njega tražimo da ne otvara fajl sa lozinkom.

Nemam Claude Code. Mogu li bez njega?

Možeš, ako imaš Python 3 (na Macu ga ponudi i sam sistem). Sačuvaj skript ispod kao posalji.py u folder gotv (kao običan tekst), pa u terminalu, u tom folderu, pokreni:

python3 posalji.py kontakti.csv --od ti@gmail.com --potpis "Tvoje Ime" --suvo
python3 posalji.py kontakti.csv --od ti@gmail.com --potpis "Tvoje Ime" --proba
python3 posalji.py kontakti.csv --od ti@gmail.com --potpis "Tvoje Ime" --posalji
#!/usr/bin/env python3
"""
GOTV mejl, 2. oktobar 2026: šalje svakom kontaktu poseban mejl sa tvog Gmail naloga.

Bez BCC-a: svaki primalac dobija svoj mejl, samo njegova adresa je u „To“.
Radi samo sa Python standardnom bibliotekom (ništa ne treba instalirati).

Lozinka (Gmail „app password“, 16 slova) se čita, tim redom, iz:
  1. promenljive GMAIL_APP_PASSWORD
  2. fajla lozinka.txt u istom folderu (napravi ga sam/a; Claude ga ne treba otvarati)
  3. pitanja u terminalu (ako skript pokrećeš sam/a)

Primeri:
  python3 posalji.py kontakti.csv --od ti@gmail.com --potpis "Marko" --suvo
  python3 posalji.py kontakti.csv --od ti@gmail.com --potpis "Marko" --proba
  python3 posalji.py kontakti.csv --od ti@gmail.com --potpis "Marko" --posalji

kontakti.csv: prvi red su nazivi kolona. Prepoznaje: email/mejl/e-mail, ime/name/first name,
drzava/zemlja/country, grad/city. Kanada se prepoznaje po državi ili po gradu (Toronto, Vankuver,
Otava, Kalgari...). Sve ostalo dobija verziju za SAD, osim ako zadaš --zemlja.
Poslati se upisuju u poslato.csv, pa ponovno pokretanje preskače one kojima je već poslato.
"""

import argparse
import csv
import getpass
import html
import os
import re
import smtplib
import ssl
import sys
import time
from datetime import datetime
from email.message import EmailMessage
from email.utils import formataddr, make_msgid, formatdate

NASLOV = "Danas je poslednji dan: ko još nije prijavljen?"

KONZULATI = {
    "sad": [
        "- Njujork: izbori.njujork@mfa.rs",
        "- Vašington i Majami: izbori@serbiaembusa.org",
        "- Čikago, Hjuston, Los Anđeles, San Francisko, San Dijego: izbori.cikago2026@mfa.rs",
    ],
    "kanada": [
        "- Toronto: izbori@rogers.com",
        "- Otava, Vankuver i Kalgari: consular.ottawa@mfa.rs",
    ],
}

ROK = {
    "sad": "Zvanični rok je sutra u 6 popodne po njujorškom vremenu (3 popodne u Kaliforniji).",
    "kanada": "Zvanični rok je sutra u 6 popodne po torontskom vremenu (4 popodne u Kalgariju, 3 popodne u Vankuveru).",
}

# **tekst** = podebljano u HTML verziji. Prazan red = novi pasus.
TELO = """{pozdrav}

**Danas je poslednji dan za prijavu za glasanje na izborima u Srbiji.** Ako si se već prijavio/la, hvala ti! Do juče se prijavilo više od 73.000 ljudi iz dijaspore, skoro duplo više nego na prošlim izborima (oko 40.000), prema rečima člana RIK-a. Danas nam trebaš za još dve stvari.

**1. Seti se 2–3 prijatelja ili rođaka u dijaspori koji se možda još nisu prijavili, i prosledi im ovaj mejl ili poruku sa dna mejla.** Bilo gde u svetu: Nemačka, Austrija, Švajcarska, Kanada, Amerika… Skoro svako zna nekoga ko „misli da se prijavi“. Ako kažu „sutra ću“, javi im se ponovo večeras:
- ko glasa u SAD ili Kanadi: https://glasaj-usa-canada.org/zahtev
- ko glasa u bilo kojoj drugoj zemlji: https://formular.skupstinaurasejanju.org/

Formular za 15 minuta napravi gotov zahtev u PDF-u, koji se pošalje mejlom ambasadi ili konzulatu. Treba samo pasoš ili lična karta i telefon.

**2. Proveri da li je tvoj zahtev stigao: https://upit.birackispisak.gov.rs/**
Ukucaj JMBG i broj lične karte. Ako piše „glasa u inostranstvu“, sve je u redu.

Ako to ne piše, a zahtev si poslao/la pre više od nekoliko dana, ili nikad nisi dobio/la potvrdu, **piši konzulatu još danas**. Prosledi im svoj prvi mejl (da se vidi kad je poslat) i traži potvrdu prijema:
{konzulati}

Ovo nije sitnica. Na prošlim izborima ambasade i konzulati su „gubili“ zahteve poslate na vreme, uz objašnjenja poput „pao nam je server“. Ako ti ne odgovore, odgovori na ovaj mejl i borićemo se za tebe.

**Zašto danas, a ne sutra?** {rok} Ali poslednjeg dana se „izgubi“ najviše zahteva. Borićemo se i za one koji pošalju sutra, ali bolje da ne rizikuju.

A ako se ni ti još nisi prijavio/la: https://glasaj-usa-canada.org/zahtev, danas.

Srbija ne prestaje na granici.

Pozdrav,
{potpis}
Glasaj iz Amerike i Kanade
https://glasaj-usa-canada.org

---

**Poruka za prosleđivanje (kopiraj u WhatsApp, Viber ili SMS):**

Danas je poslednji dan da se prijaviš za glasanje na izborima u Srbiji! Traje 15 minuta, treba ti samo pasoš ili lična karta i telefon.
Ako glasaš u SAD ili Kanadi: glasaj-usa-canada.org/zahtev
Ako glasaš bilo gde drugde: formular.skupstinaurasejanju.org
Formular napravi gotov PDF, ti ga samo pošalješ mejlom ambasadi ili konzulatu. Uradi to danas, ne sutra.

---

Dobijaš ovaj mejl jer si u kontaktu sa koordinatorima Glasaj iz Amerike i Kanade. Ako ne želiš više ovakve poruke, odgovori „ne“."""

KANADA_GRADOVI = ("toronto", "vankuver", "vancouver", "otava", "ottawa", "kalgari", "calgary",
                  "montreal", "monreal", "edmonton", "mississauga", "misisaga", "hamilton", "winnipeg",
                  "vinipeg", "kitchener", "london, on", "surrey", "burnaby", "halifax", "victoria")


def tekst(zemlja, ime="", potpis="[Ime]"):
    ime = (ime or "").strip()
    return TELO.format(
        pozdrav=f"Zdravo {ime}," if ime else "Zdravo,",
        konzulati="\n".join(KONZULATI[zemlja]),
        rok=ROK[zemlja],
        potpis=potpis,
    )


def u_html(t):
    def red(s):
        s = html.escape(s, quote=False)
        s = re.sub(r"\*\*(.+?)\*\*", r"<b>\1</b>", s)
        s = re.sub(r"(https?://[^\s<,]+[^\s<,.])", r'<a href="\1">\1</a>', s)
        s = re.sub(r"\b([\w.+-]+@[\w-]+\.[\w.-]*\w)\b", r'<a href="mailto:\1">\1</a>', s)
        return s
    delovi = []
    for pasus in t.split("\n\n"):
        if pasus.strip() == "---":
            delovi.append('<hr style="border:none;border-top:1px solid #ddd;margin:20px 0">')
            continue
        delovi.append('<p style="margin:0 0 14px">' + "<br>".join(red(r) for r in pasus.split("\n")) + "</p>")
    return ('<div style="font-family:Arial,Helvetica,sans-serif;font-size:15px;line-height:1.5;color:#16181d;max-width:600px">'
            + "".join(delovi) + "</div>")


def u_tekst(t):
    return t.replace("**", "")


def kolona(red, *imena):
    for k, v in red.items():
        if k and k.strip().lower() in imena:
            return (v or "").strip()
    return ""


def zemlja_za(red, podrazumevana):
    d = kolona(red, "drzava", "država", "zemlja", "country").lower()
    if d:
        if d in ("ca", "kan") or d.startswith(("kanad", "canad")):
            return "kanada"
        return "sad"
    g = kolona(red, "grad", "city", "mesto").lower()
    if g and any(x in g for x in KANADA_GRADOVI):
        return "kanada"
    return podrazumevana


def ucitaj(putanja, podrazumevana):
    with open(putanja, newline="", encoding="utf-8-sig") as f:
        redovi = list(csv.DictReader(f))
    vidjeni, kontakti, lose = set(), [], 0
    for r in redovi:
        mejl = kolona(r, "email", "e-mail", "mejl", "mail", "email address", "adresa").lower()
        if not re.fullmatch(r"[^@\s,;]+@[^@\s,;]+\.[a-z]{2,}", mejl):
            lose += 1
            continue
        if mejl in vidjeni:
            continue
        vidjeni.add(mejl)
        ime = kolona(r, "ime", "name", "first name", "first_name", "firstname")
        ime = ime.split()[0] if ime else ""
        kontakti.append((mejl, ime, zemlja_za(r, podrazumevana)))
    return kontakti, lose


def vec_poslato(log):
    if not os.path.exists(log):
        return set()
    with open(log, newline="", encoding="utf-8") as f:
        return {r["email"] for r in csv.DictReader(f) if r.get("status") == "poslato"}


def upisi(log, mejl, status, napomena=""):
    novi = not os.path.exists(log)
    with open(log, "a", newline="", encoding="utf-8") as f:
        w = csv.writer(f)
        if novi:
            w.writerow(["email", "status", "vreme", "napomena"])
        w.writerow([mejl, status, datetime.now().isoformat(timespec="seconds"), napomena])


def lozinka(folder):
    p = os.environ.get("GMAIL_APP_PASSWORD", "").strip()
    if not p:
        f = os.path.join(folder, "lozinka.txt")
        if os.path.exists(f):
            p = open(f, encoding="utf-8").read().strip()
    if not p and sys.stdin.isatty():
        p = getpass.getpass("Gmail app password (16 slova, ne prikazuje se): ").strip()
    if not p:
        sys.exit("Nema lozinke. Napravi lozinka.txt u ovom folderu (samo lozinka, jedan red) ili postavi GMAIL_APP_PASSWORD.")
    return p.replace(" ", "")


def poruka(od, potpis, kome, ime, zemlja):
    t = tekst(zemlja, ime, potpis)
    m = EmailMessage()
    m["From"] = formataddr((potpis, od))
    m["To"] = kome
    m["Subject"] = NASLOV
    m["Date"] = formatdate(localtime=True)
    m["Message-ID"] = make_msgid(domain=od.split("@")[1])
    m.set_content(u_tekst(t))
    m.add_alternative(u_html(t), subtype="html")
    return m


def konekcija(od, loz):
    s = smtplib.SMTP("smtp.gmail.com", 587, timeout=30)
    s.starttls(context=ssl.create_default_context())
    s.login(od, loz)
    return s


def main():
    a = argparse.ArgumentParser(description="GOTV mejl: pojedinačno slanje sa Gmail naloga")
    a.add_argument("csv", help="fajl sa kontaktima (CSV)")
    a.add_argument("--od", required=True, help="tvoja Gmail adresa")
    a.add_argument("--potpis", required=True, help="ime u potpisu i u polju From")
    a.add_argument("--zemlja", choices=["sad", "kanada"], help="ista verzija za sve (inače po koloni drzava/grad, a podrazumevano SAD)")
    g = a.add_mutually_exclusive_group(required=True)
    g.add_argument("--suvo", action="store_true", help="ništa ne šalje: prikaže brojke i prvi mejl")
    g.add_argument("--proba", action="store_true", help="pošalje po jedan probni mejl (SAD i Kanada) samo tebi")
    g.add_argument("--posalji", action="store_true", help="pravo slanje")
    a.add_argument("--limit", type=int, default=480, help="najviše mejlova sa ovog naloga danas (podrazumevano 480; Gmail dozvoljava oko 500 dnevno)")
    a.add_argument("--pauza", type=float, default=2, help="sekundi između mejlova (podrazumevano 2)")
    a.add_argument("--log", default="poslato.csv", help="dnevnik poslatih (podrazumevano poslato.csv)")
    x = a.parse_args()

    kontakti, lose = ucitaj(x.csv, x.zemlja or "sad")
    if x.zemlja:
        kontakti = [(m, i, x.zemlja) for m, i, _ in kontakti]
    gotovi = vec_poslato(x.log)
    preostali = [k for k in kontakti if k[0] not in gotovi]
    sad = sum(1 for k in preostali if k[2] == "sad")
    print(f"Kontakata: {len(kontakti)} (neispravnih adresa preskočeno: {lose}, već poslato: {len(kontakti) - len(preostali)})")
    print(f"Za slanje: {len(preostali)}  ·  SAD: {sad}  ·  Kanada: {len(preostali) - sad}  ·  sa ovog naloga najviše {x.limit}")
    if len(preostali) > x.limit:
        print(f"PAŽNJA: {len(preostali) - x.limit} kontakata preko Gmail dnevnog limita. Daj ih drugom nalogu (isti CSV + isti poslato.csv preskače poslate).")

    if x.suvo:
        if preostali:
            m, i, z = preostali[0]
            print(f"\n--- Primer: {m} ({z}) ---\nNaslov: {NASLOV}\n")
            print(u_tekst(tekst(z, i, x.potpis)))
        print("\nNišta nije poslato (--suvo).")
        return

    loz = lozinka(os.path.dirname(os.path.abspath(__file__)))
    try:
        s = konekcija(x.od, loz)
    except smtplib.SMTPAuthenticationError:
        sys.exit("Gmail nije prihvatio prijavu. Proveri adresu i app password (ne običnu lozinku) i da je uključena dvostepena provera.")

    if x.proba:
        for z in ("sad", "kanada"):
            s.send_message(poruka(x.od, x.potpis, x.od, "Probni", z))
            print(f"Probni mejl ({z}) poslat na {x.od}. Proveri inbox (i spam) i kako izgleda na telefonu.")
        s.quit()
        return

    poslato = 0
    for n, (mejl, ime, z) in enumerate(preostali[: x.limit], 1):
        for pokusaj in (1, 2):
            try:
                s.send_message(poruka(x.od, x.potpis, mejl, ime, z))
                upisi(x.log, mejl, "poslato", z)
                poslato += 1
                print(f"[{n}/{min(x.limit, len(preostali))}] {mejl} ({z})")
                break
            except smtplib.SMTPRecipientsRefused as e:
                upisi(x.log, mejl, "odbijeno", str(e)[:200])
                print(f"[{n}] {mejl}: adresa odbijena, preskačem")
                break
            except smtplib.SMTPServerDisconnected:
                if pokusaj == 2:
                    raise
                time.sleep(5)
                s = konekcija(x.od, loz)
            except (smtplib.SMTPDataError, smtplib.SMTPSenderRefused) as e:
                upisi(x.log, mejl, "greska", str(e)[:200])
                print(f"\nGmail je odbio slanje: {e}\nVerovatno je dostignut dnevni limit ili je nalog privremeno blokiran. "
                      f"Stajem. Poslato u ovom pokretanju: {poslato}. Nastavi kasnije istom komandom (preskače poslate).")
                s.quit()
                return
        time.sleep(x.pauza)
    s.quit()
    ostalo = len(preostali) - poslato
    print(f"\nGotovo. Poslato: {poslato}. Preostalo: {ostalo}." + (" Ostatak pošalji sa drugog Gmail naloga (ista komanda, druga --od adresa i lozinka)." if ostalo else ""))


if __name__ == "__main__":
    main()

Ako ništa od toga ne ide, pošalji mejl ručno iz Gmaila, svakome posebno ili u malim grupama. Tekst je iznad.

Šta ako me neko pita šta da napiše konzulatu?
Naslov: Zahtev za glasanje u inostranstvu: molim potvrdu prijema

Poštovani,

dana [datum] poslao/la sam zahtev za glasanje u inostranstvu na izborima 25. oktobra 2026. (originalni mejl je prosleđen ispod). Do danas nisam dobio/la potvrdu prijema, a na upit.birackispisak.gov.rs se ne vodim kao birač koji glasa u inostranstvu.

Molim vas da potvrdite da je moj zahtev primljen u roku i prosleđen na rešavanje.

S poštovanjem,
[Ime i prezime], [grad u kojem glasam]
Odakle su adrese konzulata i rok?

Adrese su sa stranica ambasada i konzulata (obaveštenja o prijavi za glasanje, provereno 1. oktobra). Za Otavu je to konzularna adresa sa kontakt stranice. Rok je sa glasaj-usa-canada.org. „Pao nam je server“ je odgovor koji je jedan od nas dobio telefonom od konzulata 2017. godine. Zato ga pišemo pod navodnicima, kao izgovor, bez tvrdnje da je bilo namerno.