nntpcli

package module
v1.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 4, 2025 License: MIT Imports: 17 Imported by: 1

README

nntpcli

A NNTP client with the fastest yenc support.

Installation

To install the nntpcli package, you can use go get:

go get github.com/javi11/nntpcli

Since this package uses Rapidyenc, you will need to build it with CGO enabled

Usage

Here is a simple example of how to use the nntpcli package:

package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "strings"
    "time"

    "github.com/javi11/nntpcli"
)

func main() {
    client := nntpcli.New()
    conn, err := client.Dial(context.Background(), "news.example.com", 119)
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    err = conn.Authenticate("username", "password")
    if err != nil {
        log.Fatal(err)
    }

    err = conn.JoinGroup("misc.test")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Joined group:", conn.CurrentJoinedGroup())

    // Example of Body command
    body, err := os.Create("article_body.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer body.Close()

    _, err = conn.BodyDecoded("<message-id>", body, 0)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Article body saved to article_body.txt")

    // Example of Post command
    postContent := `From: <nobody@example.com>
Newsgroups: misc.test
Subject: Test Post
Message-Id: <1234>
Organization: nntpcli

This is a test post.
`
    err = conn.Post(strings.NewReader(postContent))
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Article posted successfully")
}

Development Setup

To set up the project for development, follow these steps:

  1. Clone the repository:
git clone https://github.com/javi11/nntpcli.git
cd nntpcli
  1. Install dependencies:
go mod download
  1. Run tests:
make test
  1. Lint the code:
make lint
  1. Generate mocks and other code:
make generate

Contributing

Contributions are welcome! Please open an issue or submit a pull request. See the CONTRIBUTING.md file for details.

License

This project is licensed under the MIT License. See the LICENSE file for details.

Documentation

Overview

Package nntpcli is a generated GoMock package.

Package nntp provides base NNTP definitions.

Package nntpcli is a generated GoMock package.

Index

Constants

View Source
const (
	Unknown             = PostingStatus(0)
	PostingPermitted    = PostingStatus('y')
	PostingNotPermitted = PostingStatus('n')
	PostingModerated    = PostingStatus('m')
)

PostingStatus values.

View Source
const (
	SegmentAlreadyExistsErrCode = 441
	ToManyConnectionsErrCode    = 502
	CanNotJoinGroup             = 411
	ArticleNotFoundErrCode      = 430
)
View Source
const NumberOfStatResParams = 3

Variables

View Source
var (
	ErrCapabilitiesUnpopulated = errors.New("capabilities unpopulated")
	ErrNoSuchCapability        = errors.New("no such capability")
	ErrNilNttpConn             = errors.New("nil nntp connection")
	ErrArticleNotFound         = errors.New("article not found")
	ErrSegmentAlreadyExists    = errors.New("segment already exists")
)

Functions

func ExampleMetricsUsage added in v1.1.0

func ExampleMetricsUsage()

ExampleMetricsUsage demonstrates how to use the metrics system

func IsArticleNotFoundError

func IsArticleNotFoundError(err error) bool

func IsSegmentAlreadyExistsError added in v0.6.0

func IsSegmentAlreadyExistsError(err error) bool

func MonitoringExample added in v1.1.0

func MonitoringExample()

MonitoringExample shows how to use metrics for monitoring

Types

type Article

type Article struct {
	// The article's headers
	Header textproto.MIMEHeader
	// The article's body
	Body io.Reader
	// Number of bytes in the article body (used by OVER/XOVER)
	Bytes int
	// Number of lines in the article body (used by OVER/XOVER)
	Lines int
}

An Article that may appear in one or more groups.

func (*Article) MessageID

func (a *Article) MessageID() string

MessageID provides convenient access to the article's Message ID.

func (*Article) String

func (a *Article) String() string

type ArticleBodyReader added in v1.0.0

type ArticleBodyReader interface {
	io.ReadCloser
	GetYencHeaders() (YencHeaders, error)
}

type Client

type Client interface {
	Dial(
		ctx context.Context,
		host string,
		port int,
		config ...DialConfig,
	) (Connection, error)
	DialTLS(
		ctx context.Context,
		host string,
		port int,
		insecureSSL bool,
		config ...DialConfig,
	) (Connection, error)
}

func New

func New(
	c ...Config,
) Client

New creates a new NNTP client

If no config is provided, the default config will be used

type Config

type Config struct {
	// KeepAliveTime is the time that the client will keep the connection alive.
	KeepAliveTime time.Duration
}

type Connection

type Connection interface {
	io.Closer
	Authenticate(username, password string) (err error)
	JoinGroup(name string) error
	BodyDecoded(msgID string, w io.Writer, discard int64) (int64, error)
	BodyReader(msgID string) (ArticleBodyReader, error)
	Post(r io.Reader) error
	CurrentJoinedGroup() string
	MaxAgeTime() time.Time
	Stat(msgID string) (int, error)
	Capabilities() ([]string, error)
	GetMetrics() *Metrics
}

type DialConfig added in v0.5.0

type DialConfig struct {
	KeepAliveTime time.Duration
	DialTimeout   time.Duration
}

type Group

type Group struct {
	Name        string
	Description string
	Count       int64
	High        int64
	Low         int64
	Posting     PostingStatus
}

Group represents a usenet newsgroup.

type Metrics added in v1.1.0

type Metrics struct {
	// contains filtered or unexported fields
}

Metrics contains lightweight connection metrics using atomic operations All operations are non-blocking and have minimal performance overhead

func NewMetrics added in v1.1.0

func NewMetrics() *Metrics

NewMetrics creates a new lightweight metrics instance

func (*Metrics) GetBytesDownloaded added in v1.1.0

func (m *Metrics) GetBytesDownloaded() int64

Quick getters for commonly needed values (single atomic load each)

func (*Metrics) GetBytesUploaded added in v1.1.0

func (m *Metrics) GetBytesUploaded() int64

func (*Metrics) GetConnectionAge added in v1.1.0

func (m *Metrics) GetConnectionAge() time.Duration

func (*Metrics) GetSnapshot added in v1.1.0

func (m *Metrics) GetSnapshot() MetricsSnapshot

GetSnapshot returns a snapshot of current metrics (only when explicitly requested)

func (*Metrics) GetTotalCommands added in v1.1.0

func (m *Metrics) GetTotalCommands() int64

func (*Metrics) IsEnabled added in v1.1.0

func (m *Metrics) IsEnabled() bool

func (*Metrics) RecordArticle added in v1.1.0

func (m *Metrics) RecordArticle()

func (*Metrics) RecordArticlePosted added in v1.1.1

func (m *Metrics) RecordArticlePosted()

func (*Metrics) RecordAuth added in v1.1.0

func (m *Metrics) RecordAuth(success bool)

func (*Metrics) RecordCommand added in v1.1.0

func (m *Metrics) RecordCommand(success bool)

func (*Metrics) RecordDownload added in v1.1.0

func (m *Metrics) RecordDownload(bytes int64)

func (*Metrics) RecordGroupJoin added in v1.1.0

func (m *Metrics) RecordGroupJoin()

func (*Metrics) RecordUpload added in v1.1.0

func (m *Metrics) RecordUpload(bytes int64)

func (*Metrics) SetEnabled added in v1.1.0

func (m *Metrics) SetEnabled(enabled bool)

Enable/Disable metrics collection

type MetricsSnapshot added in v1.1.0

type MetricsSnapshot struct {
	ConnectedAt       time.Time `json:"connected_at"`
	LastActivity      time.Time `json:"last_activity"`
	ConnectionAge     string    `json:"connection_age"`
	TotalCommands     int64     `json:"total_commands"`
	CommandErrors     int64     `json:"command_errors"`
	BytesDownloaded   int64     `json:"bytes_downloaded"`
	BytesUploaded     int64     `json:"bytes_uploaded"`
	ArticlesRetrieved int64     `json:"articles_retrieved"`
	ArticlesPosted    int64     `json:"articles_posted"`
	AuthAttempts      int64     `json:"auth_attempts"`
	AuthFailures      int64     `json:"auth_failures"`
	GroupJoins        int64     `json:"group_joins"`
	SuccessRate       float64   `json:"success_rate_percent"`
	AuthSuccessRate   float64   `json:"auth_success_rate_percent"`
}

Snapshot returns current metrics values (calculated on-demand)

type MockClient

type MockClient struct {
	// contains filtered or unexported fields
}

MockClient is a mock of Client interface.

func NewMockClient

func NewMockClient(ctrl *gomock.Controller) *MockClient

NewMockClient creates a new mock instance.

func (*MockClient) Dial

func (m *MockClient) Dial(ctx context.Context, host string, port int, config ...DialConfig) (Connection, error)

Dial mocks base method.

func (*MockClient) DialTLS

func (m *MockClient) DialTLS(ctx context.Context, host string, port int, insecureSSL bool, config ...DialConfig) (Connection, error)

DialTLS mocks base method.

func (*MockClient) EXPECT

func (m *MockClient) EXPECT() *MockClientMockRecorder

EXPECT returns an object that allows the caller to indicate expected use.

type MockClientMockRecorder

type MockClientMockRecorder struct {
	// contains filtered or unexported fields
}

MockClientMockRecorder is the mock recorder for MockClient.

func (*MockClientMockRecorder) Dial

func (mr *MockClientMockRecorder) Dial(ctx, host, port any, config ...any) *gomock.Call

Dial indicates an expected call of Dial.

func (*MockClientMockRecorder) DialTLS

func (mr *MockClientMockRecorder) DialTLS(ctx, host, port, insecureSSL any, config ...any) *gomock.Call

DialTLS indicates an expected call of DialTLS.

type MockConnection

type MockConnection struct {
	// contains filtered or unexported fields
}

MockConnection is a mock of Connection interface.

func NewMockConnection

func NewMockConnection(ctrl *gomock.Controller) *MockConnection

NewMockConnection creates a new mock instance.

func (*MockConnection) Authenticate

func (m *MockConnection) Authenticate(username, password string) error

Authenticate mocks base method.

func (*MockConnection) BodyDecoded

func (m *MockConnection) BodyDecoded(msgID string, w io.Writer, discard int64) (int64, error)

BodyDecoded mocks base method.

func (*MockConnection) BodyReader added in v1.0.0

func (m *MockConnection) BodyReader(msgID string) (ArticleBodyReader, error)

BodyReader mocks base method.

func (*MockConnection) Capabilities

func (m *MockConnection) Capabilities() ([]string, error)

Capabilities mocks base method.

func (*MockConnection) Close

func (m *MockConnection) Close() error

Close mocks base method.

func (*MockConnection) CurrentJoinedGroup

func (m *MockConnection) CurrentJoinedGroup() string

CurrentJoinedGroup mocks base method.

func (*MockConnection) EXPECT

EXPECT returns an object that allows the caller to indicate expected use.

func (*MockConnection) GetMetrics added in v1.1.0

func (m *MockConnection) GetMetrics() *Metrics

GetMetrics mocks base method.

func (*MockConnection) JoinGroup

func (m *MockConnection) JoinGroup(name string) error

JoinGroup mocks base method.

func (*MockConnection) MaxAgeTime

func (m *MockConnection) MaxAgeTime() time.Time

MaxAgeTime mocks base method.

func (*MockConnection) Post

func (m *MockConnection) Post(r io.Reader) error

Post mocks base method.

func (*MockConnection) Stat

func (m *MockConnection) Stat(msgID string) (int, error)

Stat mocks base method.

type MockConnectionMockRecorder

type MockConnectionMockRecorder struct {
	// contains filtered or unexported fields
}

MockConnectionMockRecorder is the mock recorder for MockConnection.

func (*MockConnectionMockRecorder) Authenticate

func (mr *MockConnectionMockRecorder) Authenticate(username, password any) *gomock.Call

Authenticate indicates an expected call of Authenticate.

func (*MockConnectionMockRecorder) BodyDecoded

func (mr *MockConnectionMockRecorder) BodyDecoded(msgID, w, discard any) *gomock.Call

BodyDecoded indicates an expected call of BodyDecoded.

func (*MockConnectionMockRecorder) BodyReader added in v1.0.0

func (mr *MockConnectionMockRecorder) BodyReader(msgID any) *gomock.Call

BodyReader indicates an expected call of BodyReader.

func (*MockConnectionMockRecorder) Capabilities

func (mr *MockConnectionMockRecorder) Capabilities() *gomock.Call

Capabilities indicates an expected call of Capabilities.

func (*MockConnectionMockRecorder) Close

func (mr *MockConnectionMockRecorder) Close() *gomock.Call

Close indicates an expected call of Close.

func (*MockConnectionMockRecorder) CurrentJoinedGroup

func (mr *MockConnectionMockRecorder) CurrentJoinedGroup() *gomock.Call

CurrentJoinedGroup indicates an expected call of CurrentJoinedGroup.

func (*MockConnectionMockRecorder) GetMetrics added in v1.1.0

func (mr *MockConnectionMockRecorder) GetMetrics() *gomock.Call

GetMetrics indicates an expected call of GetMetrics.

func (*MockConnectionMockRecorder) JoinGroup

func (mr *MockConnectionMockRecorder) JoinGroup(name any) *gomock.Call

JoinGroup indicates an expected call of JoinGroup.

func (*MockConnectionMockRecorder) MaxAgeTime

func (mr *MockConnectionMockRecorder) MaxAgeTime() *gomock.Call

MaxAgeTime indicates an expected call of MaxAgeTime.

func (*MockConnectionMockRecorder) Post

Post indicates an expected call of Post.

func (*MockConnectionMockRecorder) Stat

func (mr *MockConnectionMockRecorder) Stat(msgID any) *gomock.Call

Stat indicates an expected call of Stat.

type Option

type Option func(*Config)

type PostingStatus

type PostingStatus byte

PostingStatus type for groups.

func (PostingStatus) String

func (ps PostingStatus) String() string

type TimeData

type TimeData struct {
	Milliseconds int64
	Bytes        int
}

type YencHeaders added in v1.0.0

type YencHeaders struct {
	FileName   string
	FileSize   int64
	PartNumber int64
	TotalParts int64
	Offset     int64
	PartSize   int64
	Hash       uint32
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL