Hunt down social media accounts by username across social networks
—
Pluggable notification handlers for processing and displaying search results. The notification system provides a flexible architecture for customizing how search results are presented to users, supporting various output formats and interaction patterns.
Abstract base class that defines the notification interface for handling search results during and after the search process.
class QueryNotify:
"""
Base class for query result notifications.
Defines the interface for notifying callers about query results.
Intended to be inherited by specific notification implementations.
"""
def __init__(self, result: QueryResult = None):
"""
Create Query Notify Object.
Args:
result: QueryResult object containing initial results (optional)
"""
result: QueryResult # Current result being processed
def start(self, message: str = None):
"""
Notify start of query process.
Called once before any queries are performed.
Override in subclasses for custom start behavior.
Args:
message: Context message for start of query (optional)
"""
def update(self, result: QueryResult):
"""
Notify query result update.
Called for each query result as it becomes available.
Override in subclasses for custom result processing.
Args:
result: QueryResult object containing results for this query
"""
def finish(self, message: str = None):
"""
Notify end of query process.
Called once after all queries have been performed.
Override in subclasses for custom finish behavior.
Args:
message: Context message for end of query (optional)
"""
def __str__(self) -> str:
"""
Convert object to string representation.
Returns:
String representation of current result
"""Concrete implementation that outputs search results to the console with colorized formatting and optional web browser integration.
class QueryNotifyPrint(QueryNotify):
"""
Query notification implementation that prints results to console.
Provides colorized output with configurable verbosity and filtering options.
"""
def __init__(
self,
result: QueryResult = None,
verbose: bool = False,
print_all: bool = False,
browse: bool = False
):
"""
Create Query Notify Print Object.
Args:
result: QueryResult object containing initial results (optional)
verbose: Boolean indicating whether to show response times and verbose output
print_all: Boolean indicating whether to print all sites including not found
browse: Boolean indicating whether to open found sites in web browser
"""
verbose: bool # Show detailed output including response times
print_all: bool # Show results for all sites, including failures
browse: bool # Open found URLs in web browser
def start(self, message: str):
"""
Print query start notification.
Displays colored header with username being searched.
Args:
message: String containing username that queries are about
"""
def update(self, result: QueryResult):
"""
Print individual query result.
Displays result with appropriate coloring based on status:
- Green for claimed/found accounts
- Red for errors and not found (if print_all enabled)
- Includes response time if verbose mode enabled
- Opens URLs in browser if browse mode enabled
Args:
result: QueryResult object containing results for this query
"""
def finish(self, message: str = "The processing has been finished."):
"""
Print completion notification.
Displays summary with total number of accounts found.
Args:
message: Completion message to display
"""
def countResults(self) -> int:
"""
Count and return number of results found so far.
Increments global result counter each time called.
Returns:
Integer count of results processed
"""
def __str__(self) -> str:
"""
Convert object to string representation.
Returns:
String representation of current result
"""globvar: int # Global variable to count the number of resultsfrom sherlock_project.sherlock import sherlock
from sherlock_project.notify import QueryNotifyPrint
from sherlock_project.sites import SitesInformation
# Create basic console notification handler
notify = QueryNotifyPrint()
# Load sites and perform search
sites = SitesInformation()
results = sherlock("john_doe", sites.sites, notify)
# Output will automatically be printed during search:
# [*] Checking username john_doe on:
# [+] GitHub: https://github.com/john_doe
# [+] Twitter: https://twitter.com/john_doe
# [*] Search completed with 2 results# Enable verbose mode to show response times
notify = QueryNotifyPrint(verbose=True)
results = sherlock("username", sites.sites, notify)
# Output includes timing information:
# [+] [245ms] GitHub: https://github.com/username
# [+] [1.2s] Twitter: https://twitter.com/username# Show all sites including those where username was not found
notify = QueryNotifyPrint(print_all=True, verbose=True)
results = sherlock("username", sites.sites, notify)
# Output shows all attempted sites:
# [+] [245ms] GitHub: https://github.com/username
# [-] [180ms] Instagram: Not Found!
# [-] Facebook: Connection Error
# [+] [320ms] Twitter: https://twitter.com/username# Automatically open found profiles in web browser
notify = QueryNotifyPrint(browse=True, verbose=True)
results = sherlock("username", sites.sites, notify)
# Found URLs will automatically open in default web browserfrom sherlock_project.notify import QueryNotify
from sherlock_project.result import QueryStatus
import logging
class QueryNotifyLogger(QueryNotify):
"""Custom notification handler that logs results."""
def __init__(self):
super().__init__()
logging.basicConfig(level=logging.INFO)
self.logger = logging.getLogger('sherlock')
self.found_count = 0
def start(self, message):
self.logger.info(f"Starting search for username: {message}")
self.found_count = 0
def update(self, result):
if result.status == QueryStatus.CLAIMED:
self.found_count += 1
self.logger.info(f"FOUND: {result.site_name} - {result.site_url_user}")
elif result.status == QueryStatus.UNKNOWN:
self.logger.warning(f"ERROR: {result.site_name} - {result.context}")
def finish(self, message=None):
self.logger.info(f"Search completed. Found {self.found_count} accounts.")
# Use custom handler
custom_notify = QueryNotifyLogger()
results = sherlock("username", sites.sites, custom_notify)class QueryNotifySilent(QueryNotify):
"""Notification handler that collects results without output."""
def __init__(self):
super().__init__()
self.results = []
self.start_time = None
def start(self, message):
import time
self.start_time = time.time()
self.results = []
def update(self, result):
self.results.append({
'site': result.site_name,
'username': result.username,
'status': result.status.value,
'url': result.site_url_user,
'response_time': result.query_time,
'context': result.context
})
def finish(self, message=None):
import time
total_time = time.time() - self.start_time
claimed = len([r for r in self.results if r['status'] == 'Claimed'])
print(f"Silent search completed: {claimed} accounts found in {total_time:.2f}s")
# Use for batch processing without console spam
silent_notify = QueryNotifySilent()
results = sherlock("username", sites.sites, silent_notify)
# Access collected results
for result in silent_notify.results:
if result['status'] == 'Claimed':
print(f"Found: {result['site']} - {result['url']}")import json
from datetime import datetime
class QueryNotifyFileExport(QueryNotify):
"""Notification handler that exports results to file."""
def __init__(self, output_file="sherlock_results.json"):
super().__init__()
self.output_file = output_file
self.session_data = {
'timestamp': datetime.now().isoformat(),
'results': []
}
def start(self, message):
self.session_data['username'] = message
print(f"Searching for {message}...")
def update(self, result):
self.session_data['results'].append({
'site_name': result.site_name,
'username': result.username,
'url': result.site_url_user,
'status': result.status.value,
'query_time': result.query_time,
'context': result.context
})
# Print only found accounts
if result.status == QueryStatus.CLAIMED:
print(f"✓ {result.site_name}: {result.site_url_user}")
def finish(self, message=None):
# Save results to file
with open(self.output_file, 'w') as f:
json.dump(self.session_data, f, indent=2)
claimed = len([r for r in self.session_data['results'] if r['status'] == 'Claimed'])
print(f"Search completed. {claimed} accounts found.")
print(f"Results saved to {self.output_file}")
# Use for automated result collection
export_notify = QueryNotifyFileExport("john_doe_results.json")
results = sherlock("john_doe", sites.sites, export_notify)Install with Tessl CLI
npx tessl i tessl/pypi-sherlock-project