-
Notifications
You must be signed in to change notification settings - Fork 65
feat: advanced job filters #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,8 @@ import { ErrorType } from '@/utils/errors' | |
| import extension from '@/utils/extension' | ||
| import stateStorage from '@/utils/globalState' | ||
| import jobStorage from '@/utils/jobs' | ||
| import jobFiltersStorage from '@/utils/jobFilters' | ||
| import { filterJobs } from '@/utils/matchJob' | ||
| import logger from '@/utils/logger' | ||
| import notifications from '@/utils/notifications' | ||
| import { captureEvent, captureException } from '@/utils/sentry' | ||
|
|
@@ -104,19 +106,23 @@ const fetchJobs = async () => { | |
| (job) => job.ciphertext | ||
| ) | ||
|
|
||
| const newJobs = newBatch.filter( | ||
| (job) => !oldBatchIds.includes(job.ciphertext) | ||
| ) | ||
|
|
||
| const newProcessedBatch = [ | ||
| ...newBatch | ||
| .filter((job) => !oldBatchIds.includes(job.ciphertext)) | ||
| .map((job) => ({ ...job, __isSeen: false })), | ||
| ...newJobs.map((job) => ({ ...job, __isSeen: false })), | ||
| ...(oldBatch ?? []), | ||
| ].slice(0, 50) | ||
|
Comment on lines
113
to
116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Use the normalized The code detects invalid Proposed fix+ const validOldBatch = Array.isArray(oldBatch) ? oldBatch : []
+
- const oldBatchIds = (Array.isArray(oldBatch) ? oldBatch : []).map(
+ const oldBatchIds = validOldBatch.map(
(job) => job.ciphertext
)
...
- ...(oldBatch ?? []),
+ ...validOldBatch,🤖 Prompt for AI Agents |
||
|
|
||
| // Advanced filters refine what notifies and what's counted; every job is | ||
| // still stored so the list can be un-filtered without re-fetching. | ||
| const filters = await jobFiltersStorage.get() | ||
|
|
||
| const unseenJobs = newProcessedBatch.filter((job) => !job.__isSeen) | ||
| const unseenCount = unseenJobs.length | ||
| const unseenCount = filterJobs(unseenJobs, filters).length | ||
|
|
||
| const hasNewUnseenJobs = | ||
| unseenJobs.length > 0 && | ||
| unseenJobs.some((job) => !oldBatchIds.includes(job.ciphertext)) | ||
| const hasNewUnseenJobs = filterJobs(newJobs, filters).length > 0 | ||
|
Comment on lines
113
to
+125
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
api_file="$(fd -t f -a 'upwork\.ts$' . | head -n 1)"
test -n "$api_file"
ast-grep outline "$api_file" --items all --match 'getJobs' --view expanded
rg -n -C 10 'getJobs|limit|pageSize|slice|take|MAX' "$api_file"Repository: neeilya/upwork-toolkit Length of output: 2843 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== fetchJobs outline =="
ast-grep outline entrypoints/background/fetchJobs.ts --view expanded || true
echo "== fetchJobs relevant lines =="
cat -n entrypoints/background/fetchJobs.ts | sed -n '80,140p'
echo "== requestJobs relevant lines =="
cat -n api/upwork.ts | sed -n '240,308p'
echo "== search for fetchJobs usages/tests =="
rg -n -C 5 'fetchJobs|getJobs|newProcessedBatch|hasNewUnseenJobs|unseenCount' .Repository: neeilya/upwork-toolkit Length of output: 16948 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== feedOptions and response types =="
cat -n api/upwork.ts | sed -n '1,140p'
echo "== graphql best matches response type search =="
rg -n -C 4 'type BestMatches|interface BestMatches|const Best|query.*Best|bestMatchJobsFeed' api/upwork.ts entrypoints . --glob '!node_modules' --glob '!dist'Repository: neeilya/upwork-toolkit Length of output: 9714 Align notification eligibility with the retained batch.
🤖 Prompt for AI Agents |
||
|
|
||
| await Promise.all([ | ||
| jobStorage.save(newProcessedBatch), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: neeilya/upwork-toolkit
Length of output: 2830
🏁 Script executed:
Repository: neeilya/upwork-toolkit
Length of output: 23123
🏁 Script executed:
Repository: neeilya/upwork-toolkit
Length of output: 16764
🌐 Web query:
Upwork jobs feed API ciphertext job id response schema updated job💡 Result:
The Upwork API uses a ciphertext-based identifier (e.g., ~01abc...) as the stable, public-facing unique identifier for job postings [1][2][3]. This format is the standard for both the modern GraphQL API and historical data references [1][4][2]. Key details regarding the ciphertext job ID and API schema: 1. Identification: The ciphertext ID is an alias for the job's unique identifier and is typically formatted with a leading tilde (e.g., ~01... or ~02...) [1][5][3]. It serves as the primary key when interacting with job-related endpoints, such as retrieving job details [1][4][2]. 2. API Schema Context: Upwork transitioned from a legacy REST API to a GraphQL-based API, which is now the primary interface [6][7]. In the GraphQL schema, job postings are queried using this ID (often labeled as jobPostingId or similar in specific query arguments) [4]. 3. Response Structure: When fetching job postings—such as through marketplace queries—the response schema typically includes the ciphertext alongside other fields like title, description, and publication date [4][8]. While specific internal schemas may evolve, the ciphertext remains the consistent, required identifier for referencing a specific job posting [9][4]. 4. Usage: Developers should use the ciphertext ID for operations such as fetching full job details, applying to jobs, or identifying job events in webhooks [2][3]. When using these identifiers in URL parameters or API requests, ensure they are correctly handled (e.g., URL-encoding the leading tilde if necessary) [3]. For official integration, developers are directed to the Upwork Developer documentation, which provides the current GraphQL schema definitions and query examples [4][7].
Citations:
Merge refreshed job snapshots before trimming the batch.
upworkApi.getJobsfetches feed snapshots, not deltas, andciphertextis the job identifier. Jobs whose updated fields appear innewBatchget dropped by the old-duplicate filter, so filters and storage can use stale client metrics,isApplied, orpremium. Preserve__isSeen, then update matching existing jobs with refreshed values before.slice(0, 50).🤖 Prompt for AI Agents