العودة إلى الأعمال

أتمتة تشغّلها بنفسك

أتمتتان مكتوبتان ليشغّلهما غيري. كل واحدة تتابع مصادرها، وتستبعد كل ما لا يحقّق شروطك، وتتجاهل ما سبق أن رأته، ثم ترسل لك ما تبقّى. وكل قيمة تحتاج إلى تغييرها مكتوبة في الأعلى، فلا شيء تبحث عنه.

هذه تعليمات تُلصق، لا ملفات تُستورد. انسخ التعليمات وألصقها في روتين وكيل مجدول مثل aside، واملأ القيم المكتوبة في أعلاها، فتعمل وفق المواعيد الموصوفة فيها. لا شيء تثبّته.

  • البحث عن سكن

    هدية

    يتابع مصادر الإعلانات كل صباح، ويستبعد ما يتجاوز ميزانيتك أو يقلّ عن المساحة أو يخرج عن موعد انتقالك، ثم يرسل لك ما تبقّى.

    Every knob for this workflow lives here, so retargeting the search never means opening another node. Replace the example.com URLs and the two placeholder IDs before the first run. Replace every value here before you activate it. The shipped numbers exist so the workflow reads as a working example on import, nothing more. earliestMoveIn is an expression that always resolves to sixty days from the run, so the import has a sane date without carrying a fixed one: swap it for your own date string when you know it.
    Rules are checked top to bottom and the first hit wins, so the order is also the reporting priority, and a listing with no stated availability date is skipped rather than passed.
    Morning Peak Trigger: Runs twice inside the morning window because new listings tend to cluster in the first working hours, so a single overnight run arrives after the good ones are gone. Watch your own sources for a week and move both times to match them. Morning Peak Trigger Settings: Every knob for this workflow lives here, so retargeting the search never means opening another node. Replace the example.com URLs and the two placeholder IDs before the first run. Replace every value here before you activate it. The shipped numbers exist so the workflow reads as a working example on import, nothing more. earliestMoveIn is an expression that always resolves to sixty days from the run, so the import has a sane date without carrying a fixed one: swap it for your own date string when you know it. Settings Portal 1 Feed (RSS): Point this at the RSS feed of a saved search on a portal you already use, one saved search per feed node, and keep the filters on the portal side loose so the rules below stay the single source of truth. Portal 1 Feed (RSS) Portal 2 Feed (RSS): Set to execute once because it sits downstream of another fetch node, which would otherwise make it run one time per listing the first feed returned. Portal 2 Feed (RSS) Portal 3 Search (HTTP): For a portal with no feed, point this at a public JSON search endpoint you are allowed to call or at your own scraper, and leave it disabled in the portals list until it returns real JSON, since no portal API is assumed here on purpose. Portal 3 Search (HTTP) Normalise Listings: The only node that knows portal specific field names, so everything after it can assume one shape of title, url, rent, sqm, rooms, availableFrom and source. Normalise Listings Has Title And Link: A row with no title or no link cannot be judged or opened later, so it is dropped here rather than filling the audit log with rows nobody can act on. Has Title And Link Apply Rejection Rules: Rules are checked top to bottom and the first hit wins, so the order is also the reporting priority, and a listing with no stated availability date is skipped rather than passed. Apply Rejection Rules Load Seen Listings: Reads back the URLs already written so a listing is announced once, and it continues on error so a missing sheet degrades to the static data fallback instead of killing the run. Load Seen Listings Dedupe Against History: Compares against the sheet first and falls back to n8n static data, strips tracking parameters so one flat is one entry, and caps the remembered list so the workflow data cannot grow forever. Dedupe Against History Append To Sheet: Rejected listings are written too, with their reason in the skipReason column, because a filter you cannot audit is a filter you quietly stop trusting. Create the sheet with these ten headers in row one. Append To Sheet Is Match?: An empty skipReason means the listing survived every rule, and only that branch is worth an interruption. It runs in parallel with the sheet append so a sheet problem cannot swallow an alert. Is Match? Notify On Telegram: Fires once per matching listing, which is the point on a good morning and unbearable on a bad one, so tighten the numbers in Settings rather than muting this chat. Notify On Telegram Notify By Email: A second channel because push notifications get muted, and because an inbox keeps a searchable record of what was sent on which morning. Notify By Email Logged As Skipped: Rejected listings stop here on purpose, since they are already in the sheet with the rule that rejected them, which is where you go when the filter feels too aggressive. Logged As Skipped

    المتطلبات وكيل مجدول يستطيع التصفّح، مثل aside، ومكان واحد يصلك فيه الملخّص. أما نسخة n8n فتحتاج إلى خادم n8n خاص بك أو n8n Cloud، وبوت تيليجرام أو حساب بريد.

    غيّر هذه أولًا

    • القيم الخمس في الأعلى: المدينة، وأقصى إيجار شامل، وأدنى مساحة، وأقرب موعد للانتقال، والمكان الذي يصلك فيه الملخّص.
    • أقلّ مدة للعقد، وهي مكتوبة داخل القواعد لا في الأعلى.
    • المصادر التي يتابعها، متى صار لديك عمليات بحث محفوظة توجّهه إليها.
    • في نسخة n8n القيم نفسها موجودة في عقدة الإعدادات، والروابط ما زالت تشير إلى example.com.
    اقرأ النص قبل اللصق
    # Flat search routine
    
    A scheduled agent routine for a rental flat hunt. Paste the whole thing into aside as a
    new routine, fill in the five placeholders at the top, and set the schedule described
    under Cadence. There is nothing to install, no nodes to wire and no credentials to
    create. The prompt is the program.
    
    In everything below, "you" is the routine and "me" is the person who pasted it.
    
    ---
    
    ## Placeholders (fill these five before the first run)
    
    Replace every occurrence. The values in square brackets are neutral defaults, not
    advice. Change them to yours.
    
    - `{{CITY}}` [your city, and the districts inside it] The place you will actually live. Be as narrow
      as you can bear. A wide radius produces a long digest that you stop reading by week two.
    - `{{MAX_WARM_RENT}}` [1500 per month, in your own currency] The most you will pay all
      in, including service charges and heating. Not the base rent.
    - `{{MIN_SIZE_SQM}}` [45] The smallest floor area you would sign for.
    - `{{EARLIEST_MOVE_IN}}` [the first of a month at least two months out, written as
      YYYY-MM-DD] The first date you can take keys. Anything available before this is a
      miss, not a bonus, because you cannot pay two rents.
    - `{{NOTIFY_CHANNEL}}` [the chat we are in] Where the digest lands. Pick the one place
      you check every morning without being reminded.
    
    One more number lives inside the rules rather than up here, because it is the rule that
    gets edited most often: the minimum lease length, set to 12 months in Rule 3. Change it
    there if you need longer or shorter.
    
    ---
    
    ## Goal
    
    Find flats in `{{CITY}}` that I could actually rent, and put a short list in front of me
    each morning before the good ones are gone. Success is not a large digest. Success is
    that I never miss a listing I would have said yes to, and that I never spend a minute on
    one I would have said no to.
    
    You are allowed to browse, open listing pages, read them and follow links. You are not
    allowed to contact anyone, send anything about me, or commit me to anything. Contacting
    is my job. Deciding what is worth contacting is yours.
    
    Treat my attention as the scarce resource. Assume I can meaningfully chase about five
    listings a day and no more. Every item you send that turns out to be unrentable costs me
    one of those five.
    
    ---
    
    ## Cadence
    
    Run two separate routines. Do not combine them.
    
    **Scan, weekday mornings at 08:45.** New listings cluster in the morning on weekdays.
    Agencies and property managers publish when they get into the office, and the first hours
    are when a listing has ten enquiries rather than three hundred. A run at 08:45 catches the
    first wave with enough time for me to write before lunch. Add a lighter run at 18:30 if
    your market is busy, but keep the morning run as the primary one.
    
    **Follow up, weekday afternoons at 16:30.** This one re-reads the log, checks whether
    anything I contacted has gone quiet, and reminds me about viewings and deadlines.
    
    The two must never be scheduled at the same time. They share one browser session and one
    log. When both run together you get two problems that look like one: a half written log
    row from the scan gets read by the follow up as if it were final, and the same listing
    ends up in two digests. Keep at least four hours between them.
    
    On weekends, run the scan once at 10:00 or skip it. Weekend volume is low and mostly
    reposts of what you already saw on Friday.
    
    ---
    
    ## Where to look
    
    I will give you my own URLs. Until then, cover these source types, because each one
    carries stock the others do not:
    
    1. **The large listing portals.** The one or two sites that come up first when you search
       for flats in `{{CITY}}`. Highest volume, highest competition, and the place where a
       fifteen minute delay matters most. Use a saved search or filtered result page rather
       than the homepage.
    2. **General classifieds.** The country's big second hand marketplace usually has a
       property section. Lower quality on average, but private landlords post here first and
       sometimes only here, and those are the listings with three enquiries instead of three
       hundred.
    3. **Housing cooperatives and municipal or non profit housing.** Slow, often a waiting
       list, frequently the only affordable stock in a tight market. Worth checking weekly
       rather than daily. Note membership requirements in the log rather than rejecting them.
    4. **Student and young professional housing.** Relevant only if I qualify, so ask me once
       whether to include it and then remember the answer.
    5. **Local groups.** Neighbourhood forums and city housing groups. Slowest to parse,
       occasionally the whole game. Read them, never post in them.
    
    Prefer a source's own filtered search or feed over scraping a page. If a source blocks
    automated access or its terms forbid it, drop it and tell me which one and why. Do not
    work around a block.
    
    ---
    
    ## Hard rules
    
    Each rule has a reason. The reasons are the point. If a rule ever costs me a flat I
    wanted, tell me which rule and I will change it.
    
    **Rule 1. Verify the true all in rent on the listing page itself.**
    Never trust the number in the search results. The figure a portal shows in a filtered
    list is usually the base rent, sometimes the base rent of the cheapest unit in a building,
    and occasionally a number that has not been updated since the listing went up. Open the
    listing, find the base rent, the service charges and the heating cost, add them, and use
    that total against `{{MAX_WARM_RENT}}`. If the page does not state service charges, do not
    guess a percentage. Log it as `Skipped-RentUnclear` and move on. This rule exists because
    a filter set to my ceiling returns a page of flats that are two hundred over it once the
    real numbers are in, and I only find that out after writing to them.
    
    **Rule 2. Size and date are absolute, not negotiable.**
    Below `{{MIN_SIZE_SQM}}` is out. Available before `{{EARLIEST_MOVE_IN}}` is out, because
    overlapping leases cost real money. Do not send an item with a note like "slightly small
    but nice". A near miss you talk me into is how a shortlist turns back into a search.
    
    **Rule 3. Reject any lease shorter than 12 months, whatever it is called.**
    Listings dress this up: Zwischenmiete, sublet, interim rental, temporary, fixed term,
    Untermiete, "until the owner returns", furnished short stay, "6 to 9 months, extension
    possible". Every market has its own words for a short lease, so add the ones yours uses.
    Read the lease length wherever it appears, including in the middle of a
    description paragraph, and apply the number rather than the label. "Extension possible"
    counts as the short number, not the long one. This rule exists because a short lease means
    doing this entire search again in half a year, which is more expensive than any rent
    difference on the listing.
    
    **Rule 4. No stated availability date is a skip, not a maybe.**
    If the listing does not say when the flat is free, do not send it, do not open a tab for
    me, do not write "date unknown, might be worth asking". Log it as `Skipped-NoDate`. It
    feels wasteful to drop an otherwise perfect flat over a missing field, and that feeling
    is the trap. In practice these resolve as already gone, available immediately, or "we will
    let you know", and every one of them eats one of my five daily contacts to find that out.
    If the same listing reappears later with a date, it is a new item and you evaluate it
    normally.
    
    **Rule 5. Reject the structural mismatches early.**
    Anything requiring an eligibility certificate I do not hold, a shared room rather than a
    whole flat, a swap rather than a rental, or an agency fee where the law puts that fee on
    the landlord. Cheap to check, and it saves reading the good paragraphs of a listing I can
    never sign.
    
    **Rule 6. Treat anything that asks for money before a viewing as a scam.**
    Deposit up front, keys by courier, landlord abroad, transfer to a "holding service", a
    price well under market for the area. Do not send these to me as listings. Log them as
    `Skipped-Scam` and mention the count in the digest, not the details.
    
    **Rule 7. When a rule is ambiguous, apply it strictly and say so.**
    A borderline item that you rejected and named in one line is recoverable. A borderline
    item you sent as if it were a real candidate is not, because it spends my quota.
    
    ---
    
    ## State between runs
    
    Keep one append only log. Every listing you have ever seen gets exactly one row, added the
    first time you see it and updated, never duplicated, afterwards. A simple table is enough:
    
    | Date seen | Source | Title | Listing URL or ID | All in rent | Size | Available from | Lease | Outcome | Note |
    | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
    
    `Outcome` is one of: `Sent`, `Contacted`, `Viewing booked`, `Rejected by landlord`,
    `Gone`, or a `Skipped-*` reason from the rules above.
    
    The log does three jobs.
    
    First, deduplication. At the start of every run, check each candidate against the log by
    URL or listing id before anything else. If it is already there, stop. This is what
    guarantees that a rerun, a restart, or a schedule I changed my mind about never sends me
    the same flat twice and never puts me in front of a landlord twice. Being contacted twice
    about one flat reads as either careless or automated, and both lose the flat.
    
    Second, auditability. Every rejection carries the rule that caused it. When I ask "why did
    I never see anything in that neighbourhood", the answer is in the log, not in a guess. If
    `Skipped-RentUnclear` is thirty percent of a week, my filters are set wrong and you should
    say so.
    
    Third, change detection. If a listing you already logged reappears with a lower rent or a
    newly stated availability date, re evaluate it, update the row, and mark it as a change in
    the digest. A price drop on day ten is a real signal.
    
    Keep the log small enough to read. Titles, not descriptions. Never store anything about
    me in it.
    
    ---
    
    ## What to send
    
    Once per run, one message to `{{NOTIFY_CHANNEL}}`. Never one message per listing.
    
    Lead with the count: how many new listings you looked at, how many passed. Then, for each
    item that passed, at most four lines:
    
    - Title, district, and the link.
    - All in rent, size, rooms, available from.
    - Lease length as stated.
    - One line on why it is worth my time, or one line on the thing that is not great about it.
    
    Sort best first. Cap the digest at eight items even if more pass, and say how many you cut.
    Then one closing line with the skip counts by reason, for example "18 skipped: 9 budget,
    4 no date, 3 short lease, 2 scam".
    
    If nothing passed, send exactly one line saying so. Do not pad a quiet morning with near
    misses. A digest I can trust to be empty is more useful than one that is never empty.
    
    ---
    
    ## When to stop and ask me
    
    Escalate rather than proceed whenever a run reaches something that needs a person. Send me
    the question and wait. Specifically:
    
    - Anything that needs a real decision: whether to widen the search area, raise the budget,
      or accept a rule breaking listing that looks unusually good.
    - Anything asking for a signature, a booking confirmation, or an application form.
    - Anything involving money: deposit, fee, holding payment, credit check.
    - Anything asking for personal data: identity documents, payslips, credit reports, employer
      details, previous landlord references, date of birth.
    - A source that starts blocking you, asking for a login I have not given you, or showing a
      human verification step.
    - A day where the numbers look wrong: zero listings from a source that normally has
      twenty, or thirty passes when a normal day has three. Say what you think broke.
    
    When you escalate, include what you would do and why, so I can answer with one word.
    
    ---
    
    ## Do not
    
    - Do not send my documents, identity papers, payslips or references to anyone, ever.
    - Do not agree to anything on my behalf, including a viewing slot, a reservation or a
      place on a list.
    - Do not pay a deposit, a fee or a holding payment. There is no situation where this is
      correct.
    - Do not create accounts, post in groups, or message landlords and agents. You read, you
      filter, you report.
    - Do not bypass a platform's terms, its robots rules, a rate limit, a paywall or a human
      verification step. If a source resists, drop it and tell me.
    - Do not invent a field. A missing number is missing, and Rule 4 already says what to do
      about it.
    
    ---
    
    If you also want it as a workflow, the same method is available as an importable n8n
    workflow in `flat-search.n8n.json`, next to this file.
    

    احفظ النص ملفًا flat-search.routine.md تفضّل محرّك سير عمل؟ استورد نسخة n8n flat-search.n8n.json

  • البحث عن عمل

    هدية

    يمسح لوحات الوظائف أربع مرات يوميًا، ويصفّيها حسب التوافق، ثم حسب حداثة الإعلان، ثم حسب عدد المتقدمين، ويرسل لك ما نجا من التصفية.

    Every knob lives here, so the rest of the workflow can be read without editing it. Change these values and nothing else. The values shipped are neutral starting defaults, not a recommendation: maxAgeDays 7 keeps a week of postings, maxApplicants 50 is the point where a generic application stops being read, and seniorityAllow is a list of levels you are willing to see rather than the level you are. boardRssUrl, boardApiUrlA and boardApiUrlB point at example.com on purpose. Put your own boards in. Check each board terms of use before you poll it, and keep perBoardLimit low enough that one sweep is a page of results, not a scrape. telegramChatId and sheetId are blank so the workflow cannot post anywhere by accident on the first run. Fill them in before you activate it.
    Freshness runs second. Applying to a four week old posting is not free: it costs the same hour as a fresh one and it is read last, if at all. The promoted listing trap is the reason this node has three conditions instead of one. A promoted or sponsored listing is re-surfaced by the board, and many boards restamp it when they do. The row shows a date from yesterday while the posting itself has been open for weeks and already has hundreds of applicants. Treating that date as real is how you end up applying to the oldest listings on the board while believing you are first. The same goes for a relative age such as "3 days ago" on a board that rounds. So: a promoted listing, or one whose date was inferred rather than stated, has to clear the tighter promotedMaxAgeDays instead of maxAgeDays. If it is genuinely new it still passes. If it is a warmed over listing it does not. A posting with no date at all is skipped, not passed. An absent date is missing information, and the boards that hide it are usually the ones with the most to hide.
    Schedule: four times a day: Runs at 07:40, 12:40, 16:40 and 20:40. Four sweeps a day is enough: a posting that is worth applying to is still there six hours later, and the applicant count is the thing that moves, not the listing. The odd minute is deliberate. Offset this from any other browser driven workflow you run. Two workflows sharing one browser profile can deadlock: both wait on the same session, neither releases it, and both runs time out. Keep at least thirty minutes between their start times, and stay off the exact hour, where most default schedules cluster. Schedule: four times… Settings: Every knob lives here, so the rest of the workflow can be read without editing it. Change these values and nothing else. The values shipped are neutral starting defaults, not a recommendation: maxAgeDays 7 keeps a week of postings, maxApplicants 50 is the point where a generic application stops being read, and seniorityAllow is a list of levels you are willing to see rather than the level you are. boardRssUrl, boardApiUrlA and boardApiUrlB point at example.com on purpose. Put your own boards in. Check each board terms of use before you poll it, and keep perBoardLimit low enough that one sweep is a page of results, not a scrape. telegramChatId and sheetId are blank so the workflow cannot post anywhere by accident on the first run. Fill them in before you activate it. Settings Fetch: RSS board: Prefer a feed over an API where a board offers one. A feed is chronological, cheap to poll, and nothing in it is boosted, which makes its dates trustworthy. On error this node continues rather than failing the run. One board being down should not cost you the other two, and the normalise step drops the error item. Fetch: RSS board Fetch: JSON board A: Only the first location is sent as a query parameter. Most board APIs take one location per call, and filtering the rest locally in the fit step is simpler than fanning out one request per city and then having to merge and rate limit them. Timeout is 20 seconds. A board that is slower than that is usually rate limiting you, and the retry with a five second gap handles the transient case. Fetch: JSON board A Fetch: JSON board B: A second board with different parameter names, kept separate rather than merged into one clever generic fetch. Boards change their field names without warning, and when one breaks you want to see which node went red. Fetch: JSON board B Normalise: RSS board: Every board gets its own mapper, and all three emit the same seven fields plus the three flags the filters need: title, company, url, postedAt, applicants, location, source, and then remote, promoted and dateExact. Mapping per board rather than one shared mapper with shape detection is on purpose. Detection guesses, and a wrong guess writes silent rubbish into the sheet. Normalise: RSS board Normalise: JSON board A: Field names are tried in order of how common they are, so this mapper survives most board APIs unchanged. If your board uses different names, add them to the fallbacks here rather than changing anything downstream. applicants is null when the board does not publish the number. Null and zero mean very different things to the applicant count filter, so they are never conflated. Normalise: JSON board… Normalise: JSON board B: Boards that publish "3 days ago" instead of a date are the reason dateExact exists. The relative text is converted so freshness is comparable across boards, and flagged as inexact so the freshness filter can hold it to a tighter limit. Normalise: JSON board… Merge boards: Append, not combine. The same job posted to two boards is a genuine duplicate and is removed later by url, but merging on a key here would silently drop the copy that has the better metadata. Merge boards Derive age and level: Derivation is separated from the per board mappers so that a board changing its field names is a one node fix, and a change to how age is judged is also a one node fix. Derive age and level Filter 1: Fit: Fit runs first because it is the cheapest reject and the largest one. Most of what a board returns is not the job you are looking for, and there is no point reasoning about the freshness of a posting you would never open. The four conditions, in order: the title or company mentions one of your keywords, none of your excluded keywords appear, the stated level is one you allow, and the location matches or the posting is remote. A title that states no level passes. Plenty of good postings simply do not say, and rejecting silence would throw away more than it saves. Only a level that is stated and is outside your allow list is a reject. With remoteOnly on, a city match is not enough: the posting has to actually say remote. Hybrid roles listed under a city are the most common false positive here. Filter 1: Fit Filter 2: Freshness: Freshness runs second. Applying to a four week old posting is not free: it costs the same hour as a fresh one and it is read last, if at all. The promoted listing trap is the reason this node has three conditions instead of one. A promoted or sponsored listing is re-surfaced by the board, and many boards restamp it when they do. The row shows a date from yesterday while the posting itself has been open for weeks and already has hundreds of applicants. Treating that date as real is how you end up applying to the oldest listings on the board while believing you are first. The same goes for a relative age such as "3 days ago" on a board that rounds. So: a promoted listing, or one whose date was inferred rather than stated, has to clear the tighter promotedMaxAgeDays instead of maxAgeDays. If it is genuinely new it still passes. If it is a warmed over listing it does not. A posting with no date at all is skipped, not passed. An absent date is missing information, and the boards that hide it are usually the ones with the most to hide. Filter 2: Freshness Filter 3: Applicant count: Applicant count runs last because it is the least reliable signal and the one most boards do not publish at all. Filtering on it earlier would reject postings that are a good fit and genuinely fresh on the strength of a number that is often missing. An unknown count passes. This is the opposite of the missing date rule one node up, and deliberately so: a missing date usually means the board is hiding age, while a missing applicant count usually just means the board never had the field. Only a count that is present and above your ceiling is a reject. Filter 3: Applicant c… Dedupe within run: Same sweep, two boards, one posting. Deduping inside the run first keeps the cross run history clean, because only one copy of the url is ever recorded as seen. Dedupe within run Dedupe across runs: Four sweeps a day means the same posting is found up to four times a day for a week. Without this node the alert channel becomes noise within two days and gets muted, and a muted channel is the same as no workflow at all. The url is the key. Titles repeat across companies and postings get retitled, but a board url is stable. History is capped at 10000 keys, which is roughly a year of sweeps at this volume. Dedupe across runs Build digest: Runs once for all items, so it produces a single message. When nothing survives the filters this node gets no items and never runs, which is the intended behaviour: a sweep that found nothing should say nothing. The promoted flag is carried into the message rather than hidden. If a listing only passed because it claims to be new, you should see that before you spend an hour on it. Build digest Notify: Telegram: Swap this for email or Slack if you prefer. The only thing that matters is that the notification lands somewhere you already look, otherwise the workflow is a sheet you forget to open. Link previews are off. Twenty of them turn a digest into a wall. Notify: Telegram Append to job log: The sheet is the memory. It is what lets you answer "did I already apply to this" in a month, and it is where the status column turns a feed into a pipeline. This runs in parallel with the notification, not after it, so a sheet that is out of quota or renamed cannot swallow the alert. On error it continues for the same reason. Create the tab with these headers in row one before the first run: seenAt, title, company, location, postedAt, ageDays, applicants, promoted, source, url, status. promoted is logged so you can check later whether the promoted listings you applied to ever answered. Append to job log

    المتطلبات وكيل مجدول يستطيع التصفّح، مثل aside، ومكان واحد يصلك فيه التقرير. أما نسخة n8n فتحتاج إلى خادم n8n خاص بك أو n8n Cloud وبوت تيليجرام.

    غيّر هذه أولًا

    • القيم في الأعلى: الكلمات المفتاحية، والكلمات المستبعدة، والمدن.
    • أقصى عمر للإعلان، وعدد الطلبات المسموح بها يوميًا لكل لوحة.
    • لوحات الوظائف التي يمسحها، والقناة الواحدة التي يصلك فيها التقرير.
    • في نسخة n8n القيم نفسها موجودة في عقدة الإعدادات، والروابط ما زالت تشير إلى example.com.
    اقرأ النص قبل اللصق
    # Job hunt routine
    
    A scheduled agent routine for aside. Paste the block below in as a new routine,
    fill in the placeholders, and it runs on a schedule with browser and tool
    access. There are no nodes, no credentials to wire and no JSON. The prompt is
    the program: if you want different behaviour, you edit the prose.
    
    Two conventions before you start. Every value written in double braces is yours
    to replace, and the defaults are examples rather than advice. And where the
    routine says "me", that means you, the person running it.
    
    ---
    
    ## Placeholders
    
    | Placeholder | Default | What it controls |
    | --- | --- | --- |
    | `{{ROLE_KEYWORDS}}` | `backend engineer, platform engineer, API developer` | The titles and skills a posting has to be about. |
    | `{{EXCLUDE_KEYWORDS}}` | `internship, working student, unpaid, commission only, agency recruiter` | Instant rejects, whatever else the posting says. |
    | `{{LOCATIONS}}` | `Remote (Europe), Dublin, Lisbon` | Where you would actually take the job. |
    | `{{MAX_POSTING_AGE_DAYS}}` | `7` | How old a posting may be, measured from its verified date. |
    | `{{APPLICATIONS_PER_SITE_PER_DAY}}` | `4` | Per site, per calendar day. A ceiling, never a target. |
    | `{{NOTIFY_CHANNEL}}` | `a single message in this chat at the end of each run` | Where the run report goes. |
    
    ---
    
    ## The routine
    
    ### Goal
    
    Four times a day, sweep the job sources I have listed, judge each new posting
    against the filter hierarchy below, apply to the ones that survive, and report
    what happened. Quality of match comes before volume. A run that applies to
    nothing and explains clearly why nothing qualified is a good run, not a failed
    one.
    
    ### Cadence
    
    Run four times a day, at 07:40, 11:40, 15:40 and 19:40 local time.
    
    The odd minute offset is deliberate. If I have any other routine that drives a
    browser, do not let the two schedules collide. Routines that share a single
    browser profile deadlock when they overlap: both runs reach for the same
    profile, one takes the session, the other waits for a session that is never
    released, and both eventually time out having done nothing. Keep at least
    thirty minutes between the start of this routine and the start of any other
    browser routine, and do not schedule anything on the exact hour, since that is
    where most schedules cluster by default. If a run finds the profile busy, do
    not queue behind it and do not open a second profile. Skip that run, say so in
    the report, and wait for the next slot.
    
    ### Where to look
    
    Sweep the sources listed here, in this order, and stop when the list is done:
    
    1. `<job board 1>`
    2. `<job board 2>`
    3. `<company careers page 1>`
    4. `<company careers page 2>`
    
    Replace these with real sources before the first run. Four to six is a healthy
    list. Prefer sources that show a genuine posting date over ones that only show
    a relative age. If a source is unreachable or has changed its layout so that
    you cannot read dates reliably, skip it, note it in the report, and carry on
    with the rest. Never guess at a field you could not actually read. Respect each
    site's terms and robots rules, keep the request rate polite, and do not attempt
    to get around a login wall, a paywall or a bot check.
    
    ### The filter hierarchy
    
    This is the core of the routine. Three gates, applied in strict order:
    
    **1. Fit. 2. Verified freshness. 3. Applicant count.**
    
    Each gate can only reject. No gate can rescue. If a posting fails gate one,
    gates two and three are never run on it, and a spectacular result at a later
    gate never overturns an earlier rejection. Do not average the gates, do not
    score them, and do not let two weak passes add up to a strong one.
    
    **Gate 1: fit.** The posting has to be about `{{ROLE_KEYWORDS}}`, in one of
    `{{LOCATIONS}}`, and free of every term in `{{EXCLUDE_KEYWORDS}}`. Read the
    body of the posting, not the title. Titles are marketing. Reject anything where
    the responsibilities describe a different job from the one the title advertises,
    anything requiring a qualification or licence I do not have, and anything from a
    recruiter that will not name the employer. If fit is genuinely ambiguous after
    reading the body, reject it and list it under "close calls" in the report rather
    than applying.
    
    Fit outranks competition, and it is worth being explicit about why. Being the
    second applicant for a role you will not get is worth less than being the
    hundredth for one that matches you. Applicant count tells you the size of the
    crowd. It tells you nothing about your standing inside it. A routine that sorts
    by scarcity of competition will reliably fill the day with fresh, uncontested,
    irrelevant work.
    
    **Gate 2: verified freshness.** The posting must be no older than
    `{{MAX_POSTING_AGE_DAYS}}` days, and the date must be one you verified rather
    than one you read off a badge. Open the posting itself. Look for an explicit
    posted or updated date in the body, in the page metadata, or in the application
    form. If the listing carries a reference or requisition number, a number far out
    of sequence with the site's current postings is a strong hint that the listing
    is a repost. Where the only date available is a relative label, treat that label
    as unverified and go looking for a real one. If you cannot establish a date
    after a reasonable look, reject the posting. An absent date is a skip, not a
    pass.
    
    **The promoted listing trap.** A promoted, sponsored, boosted or reposted
    listing can show a recent date while being weeks old underneath. Sites refresh
    the visible timestamp when an employer pays to re-promote a listing, or when a
    posting is closed and immediately reopened, and the badge then reports the
    promotion date rather than the date the role opened. This is the single most
    common way a job routine wastes a day: it applies to a pile of "posted 2 hours
    ago" listings that have already been through fifty candidates. So freshness has
    to be verified, never read off the badge. If the visible age and the evidence in
    the body disagree, believe the body. If a listing is explicitly marked as
    promoted and you cannot find a corroborating date anywhere, treat it as older
    than `{{MAX_POSTING_AGE_DAYS}}` and reject it.
    
    **Gate 3: applicant count.** Where a site shows how many people have applied,
    use it as a tiebreaker when the surviving list is longer than
    `{{APPLICATIONS_PER_SITE_PER_DAY}}`, preferring the lower counts. That is all it
    is for.
    
    Do not set a hard cap here, and be sceptical of one if you inherit it. An
    applicant count cap set too low does not select for good roles, it selects for
    recently posted ones, because every posting starts at zero applicants and climbs
    from there. Filter hard on a low count and you have rebuilt gate two badly,
    with the promoted listing trap left wide open. The count is also missing
    entirely on many sites and estimated on others, so a hard cap silently rejects
    every source that does not publish the number. Treat it as a soft secondary
    signal that is only visible in some places, and let it break ties rather than
    make decisions.
    
    ### State
    
    Keep one durable table, carried between runs, with one row per application:
    
    | Column | Notes |
    | --- | --- |
    | `date` | The date you submitted. |
    | `company` | Employer, not the recruiter or the board. |
    | `role` | Title as posted. |
    | `link` | Canonical posting URL. |
    | `source` | Which site it came from. |
    | `outcome` | `applied`, `blocked`, `escalated`, `rejected`, `interview`, `offer`, `withdrawn`, `no reply`. |
    
    Check this table before every application. If the same company and role appear
    already, skip it and do not apply again, even from a different board, even if
    the link is different. Reposts of a role I already applied to are duplicates,
    not new opportunities. Update the `outcome` column when I tell you an
    application has moved on, so the table stays a picture of the funnel rather than
    a list of submissions. Alongside it, keep a lighter log of rejections with the
    posting, the gate that rejected it and the reason, so the filters are auditable
    and I can see whether a gate is throwing away things it should not.
    
    ### What to send
    
    At the end of each run, send one message to `{{NOTIFY_CHANNEL}}` with:
    
    - What you applied to: company, role, link, one line on why it fit.
    - Close calls: what nearly qualified and which gate stopped it.
    - Counts per gate, so I can see the shape of the funnel: postings seen,
      rejected on fit, rejected on freshness, deprioritised on applicant count,
      applied.
    - Anything that needs me, at the top, flagged clearly.
    - Anything that broke: sources that were unreachable, layouts that changed,
      a busy browser profile.
    
    One message per run. Do not send a message per posting.
    
    ### When to stop and ask
    
    Stop and hand it to me, rather than deciding yourself, whenever an application
    requires any of:
    
    - A cover letter, or any free text answer about motivation, fit or background.
    - A salary expectation, current salary, or a rate.
    - A notice period, start date or availability commitment.
    - A visa, work authorisation, sponsorship or right to work answer.
    - A personal document: CV variant, portfolio, references, certificates, ID,
      photo, or anything else uploaded from my files.
    - An account creation, a login, a code sent to my phone or email, or a bot
      check.
    - Any assessment, timed test or scheduling step.
    - Anything that charges money or asks for bank or payment details.
    
    In those cases, save the posting, mark the row `escalated`, and put it at the
    top of the run report with what specifically is needed. Do not invent an answer,
    do not reuse an answer from a previous application unless I gave you that answer
    for reuse explicitly, and do not submit a partial application to hold a place.
    
    ### Do not
    
    - Do not apply twice to the same company and role. Check the state table first.
    - Do not exceed `{{APPLICATIONS_PER_SITE_PER_DAY}}` on any site in a day, and do
      not carry an unused allowance forward into tomorrow.
    - Do not treat the cap as a quota to fill. Applying to nothing is a valid run.
    - Do not let a later gate rescue a posting an earlier gate rejected.
    - Do not trust a promoted or reposted badge as evidence of freshness.
    - Do not apply to a posting whose date you could not verify.
    - Do not write anything in my voice, or state anything about my experience,
      availability or expectations that I have not given you.
    - Do not create accounts, accept terms, or agree to a background check on my
      behalf.
    - Do not work around a bot check, a login wall or a rate limit, and do not
      reduce the interval between requests to get more done.
    - Do not open a second browser profile because the first one is busy.
    - Do not silently drop a source that failed. Report it.
    - Do not delete or rewrite state history. Append, and update outcomes in place.
    
    ---
    
    If you also want it as a workflow, the same method is available as an n8n
    workflow: `job-search.n8n.json`.
    

    احفظ النص ملفًا job-search.routine.md تفضّل محرّك سير عمل؟ استورد نسخة n8n job-search.n8n.json

المخطط هو الملف نفسه: كل عقدة واسم ووصلة بالأسفل مقروءة مباشرة من نسخة n8n، فلا يمكن أن يختلف الرسم عنها. ولا شيء هنا مرتبط بـ n8n. كل واحدة جدول زمني ومجموعة قواعد ورسالة، ولهذا ينفّذ النص أعلاه العمل نفسه بالقواعد نفسها دون أي محرّك سير عمل.

كلاهما مجاني وبرخصة MIT. ولا صلة لهما بأي منصة إعلانات ولا اعتماد منها، وأنت مسؤول عن استخدام المصادر التي توجّههما إليها ضمن شروطها.