# 🎯 nostr-tools v1.2.1 - Filter API Enhancement

**Release Date:** October 5, 2025
**Tag:** v1.2.1
**Commit:** TBD

---

## 🚀 Overview

This release introduces an important enhancement to the Filter API with the new `from_subscription_filter()` classmethod, enabling bidirectional conversion between Filter instances and subscription filter dictionaries. This enhancement improves developer experience when working with relay subscription responses and provides more flexibility in filter handling.

## ✨ What's New in v1.2.1

### 🔧 Filter API Enhancement

**New `Filter.from_subscription_filter()` Method**
- **Bidirectional Conversion** - Convert subscription filter dictionaries (with #-prefixed tag keys) directly to Filter instances
- **Relay Response Handling** - Simplifies parsing subscription filters from relay responses
- **Tag Key Normalization** - Automatically converts #-prefixed keys (#e, #p, etc.) to internal tag format
- **Type Safety** - Full type hints and validation for all inputs
- **Round-Trip Support** - Enables Filter → subscription_filter → Filter workflows

**Enhanced Filter Flexibility**
- Seamless conversion between internal and subscription formats
- Improved compatibility with Nostr relay subscription messages
- Easier debugging and testing with filter transformations
- Better integration with relay communication protocols

### 📚 Comprehensive Test Coverage

**17 New Test Cases**
- Basic filter creation from subscription data
- Tag filter conversion (#e, #p, #a, #t, etc.)
- Round-trip conversion validation
- Empty and invalid input handling
- Mixed tag format handling
- Edge cases (empty tags, invalid keys, non-alphabetic characters)
- Validation error handling
- Integration with existing Filter functionality

## 🔧 Technical Details

### API Addition

**New Classmethod: `Filter.from_subscription_filter()`**

```python
from nostr_tools import Filter

# Subscription filter from relay (with # prefixes)
subscription_data = {
    "kinds": [1],
    "limit": 10,
    "#e": ["event_id_123"],
    "#p": ["pubkey_abc"]
}

# Convert to Filter instance
filter = Filter.from_subscription_filter(subscription_data)

# Filter has normalized tags
assert filter.kinds == [1]
assert filter.limit == 10
assert filter.tags == {"e": ["event_id_123"], "p": ["pubkey_abc"]}
```

**Round-Trip Conversion**

```python
from nostr_tools import Filter

# Create filter with tags
original = Filter(
    kinds=[1],
    e=["event1"],
    p=["pubkey1"]
)

# Convert to subscription format
sub_filter = original.subscription_filter
# {"kinds": [1], "#e": ["event1"], "#p": ["pubkey1"]}

# Convert back to Filter
reconstructed = Filter.from_subscription_filter(sub_filter)

# Verify they match
assert reconstructed.kinds == original.kinds
assert reconstructed.tags == original.tags
```

### Implementation Details

**Tag Key Processing**
- Automatically detects and processes #-prefixed tag keys
- Strips # prefix and validates single alphabetic characters
- Filters out invalid tag names during normalization
- Preserves standard fields (ids, authors, kinds, since, until, limit)

**Validation**
- Type checking for dictionary input
- Element validation for all filter fields
- Automatic normalization of hex strings to lowercase
- Removal of empty tag lists
- Full integration with existing Filter validation

### Code Location

- **Implementation**: `src/nostr_tools/core/filter.py:334-353`
- **Tests**: `tests/unit/test_filter.py:467-619`
- **Test Class**: `TestFilterSubscriptionFilterConversion`

## 📋 Breaking Changes

**None** - This is a fully backward-compatible enhancement. All existing code continues to work without modification.

## 🎯 Migration Guide

### From v1.2.0

**No Changes Required** - v1.2.1 is fully compatible with v1.2.0. Simply upgrade:

```bash
pip install --upgrade nostr-tools==1.2.1
```

### Using the New Feature

**Before (existing approach still works):**
```python
# Manual construction
filter = Filter(
    kinds=[1],
    e=["event_id"],
    p=["pubkey"]
)
```

**After (new option available):**
```python
# From subscription filter dictionary
subscription_data = {
    "kinds": [1],
    "#e": ["event_id"],
    "#p": ["pubkey"]
}
filter = Filter.from_subscription_filter(subscription_data)
```

**Common Use Cases:**

1. **Parsing Relay Responses**
```python
# When relay sends back subscription confirmation
async def handle_relay_response(message):
    if message[0] == "REQ":
        sub_id = message[1]
        sub_filter = message[2]
        # Convert to Filter for easier handling
        filter = Filter.from_subscription_filter(sub_filter)
        # Work with filter...
```

2. **Testing and Debugging**
```python
# Create test filters from subscription format
test_cases = [
    {"kinds": [1], "#e": ["test_event"]},
    {"authors": ["a" * 64], "#p": ["test_pubkey"]},
]
filters = [Filter.from_subscription_filter(tc) for tc in test_cases]
```

3. **Configuration Files**
```python
import json
from nostr_tools import Filter

# Load filter configurations
with open("filters.json") as f:
    filter_configs = json.load(f)

filters = [Filter.from_subscription_filter(cfg) for cfg in filter_configs]
```

## 🧪 Testing & Quality

**Test Coverage**
- ✅ 541 total tests (17 new for v1.2.1)
- ✅ 80%+ code coverage maintained
- ✅ All tests passing
- ✅ Type checking with MyPy
- ✅ Linting with Ruff
- ✅ Security scanning (Bandit, Safety, pip-audit)

**New Test Coverage**
- Basic functionality tests
- Tag conversion tests
- Round-trip validation
- Error handling
- Edge cases
- Integration with existing Filter tests

## 📋 Dependencies

**No Changes**
- All dependencies remain the same as v1.2.0
- No new dependencies added
- No dependency updates required

**Runtime Dependencies**
- secp256k1 >=0.14.0, <1.0.0
- bech32 >=1.2.0, <2.0.0
- aiohttp >=3.8.0, <4.0.0
- aiohttp-socks >=0.8.0, <1.0.0

## 🚀 Development Experience

**Improved Developer Workflow**
- Easier handling of relay subscription responses
- Simplified filter conversion and transformation
- Better integration with Nostr protocol messages
- Enhanced debugging capabilities

**Documentation Updates**
- Updated CHANGELOG.md with v1.2.1 details
- Updated SECURITY.md supported versions
- Updated releases/README.md
- Updated CLAUDE.md and DEVELOPMENT.md examples

## 🤝 Contributing

We welcome contributions and feedback from the community. v1.2.1 is now the only supported version.

**Getting Started**
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Run `make check-all` to verify quality
5. Submit a pull request

**Development Commands**
```bash
# Install development dependencies
make install-dev

# Run tests
make test

# Run quality checks
make check-all

# Build documentation
make docs-build
```

## 🔐 Security

**No Security Changes**
- No security vulnerabilities fixed in this release
- All security best practices from v1.2.0 continue to apply
- For security issues, email: security@bigbrotr.com

## 📄 License

This project is licensed under the **MIT License** - see the [LICENSE](LICENSE) file for details.

## 🙏 Acknowledgments

- **Contributors** - All contributors to this project
- **Community** - Feedback and suggestions from the Nostr community
- **Python Ecosystem** - The amazing tools and libraries

## 🔗 Links

- **[PyPI Package](https://pypi.org/project/nostr-tools/)** - Install from PyPI
- **[GitHub Repository](https://github.com/bigbrotr/nostr-tools)** - Source code and issues
- **[Documentation](https://bigbrotr.github.io/nostr-tools/)** - Complete API reference
- **[Changelog](../CHANGELOG.md)** - Detailed changelog

---

**🎯 This release enhances the Filter API with bidirectional conversion capabilities, making it easier to work with Nostr relay subscription filters while maintaining full backward compatibility.**
