miq

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: MIT Imports: 31 Imported by: 0

README

makeitaquote

CI Go Reference License

メッセージから引用画像を生成するpure Goライブラリです。ローカル描画、会話画像、Discord・Misskey・X入力、Twemoji、Google Fonts、PNG/JPEG/WebP/AVIF、アセット管理CLI、Voids APIクライアントを提供します。

最低Goバージョンは1.24です。通常の描画とCLIはCGO_ENABLED=0でビルドできます。

Install

go get github.com/tikipiya/MiQ
go install github.com/tikipiya/MiQ/cmd/miq@latest

Quote image

package main

import (
	"context"
	"os"

	miq "github.com/tikipiya/MiQ"
	"github.com/tikipiya/MiQ/theme"
)

func main() {
	engine, err := miq.NewEngine(miq.EngineOptions{})
	if err != nil {
		panic(err)
	}

	img, err := engine.RenderQuote(context.Background(), miq.Quote{
		Text:        "小さな工夫が、毎日の使いやすさをつくる。",
		Username:    "sample_user",
		DisplayName: "Sample User",
		Watermark:   "Make it a Quote",
		Avatar:      miq.ImageFile("avatar.png"),
	}, miq.RenderOptions{Theme: theme.Preset(theme.Dark)})
	if err != nil {
		panic(err)
	}

	file, err := os.Create("quote.png")
	if err != nil {
		panic(err)
	}
	defer file.Close()
	if err := miq.Encode(file, img, miq.EncodeOptions{Format: miq.PNG}); err != nil {
		panic(err)
	}
}

ImageFileのほかにImageURLImageBytesImageValueを使用できます。remote画像は既定でloopback、private、link-local networkを拒否します。

Themes and output

組み込みテーマはdarklightcolorportraitportrait-lightです。theme.Inputでlayout、色、gradient、avatar、text、quote mark、divider、labelを個別に上書きできます。

	width, height := 1280, 720
	options := miq.RenderOptions{
		Theme: theme.Input{
			Extends: theme.Portrait,
			Width:   &width,
			Height:  &height,
	},
	Scale: 2,
}

出力形式はmiq.PNGmiq.JPEGmiq.WebPmiq.AVIFです。EncodeEncodeBytesEncodeDataURLを利用できます。

Source adapters

  • adapter/discord: message、member、mention、Discord Markdown、timestamp
  • adapter/misskey: note、remote handle、MFM
  • adapter/twitter: tweet、API v2、FxTwitter
  • markup/commonmark: 一般的なMarkdownのplain text化

各adapterは外部SDKに依存しない最小構造体を受け取り、miq.Quoteまたはmiq.ConversationMessageへ変換します。

Conversation

img, err := engine.RenderConversation(ctx, []miq.ConversationMessage{
	{Username: "cat", DisplayName: "Cat", Text: "first", Avatar: miq.ImageFile("cat.png")},
	{Username: "cat", DisplayName: "Cat", Text: "second", Avatar: miq.ImageFile("cat.png")},
	{Username: "dog", Text: "reply"},
}, miq.ConversationOptions{Theme: miq.ConversationDark, Width: 600})

CLI

miq generate --text "hello" --username cat --avatar avatar.png --out quote.png
miq install
miq install fonts "Dela Gothic One"
miq ls
miq env

主なコマンドはinstalluninstalllssearchoutdatedupdatepruneenvgenerateです。

Offline assets

Unicode emojiはTwemoji、<:name:id>はDiscord CDN、:name::name@host:はMisskeyから取得します。日本語フォントはsystem、disk cache、Google Fontsの順に解決されます。

miq installで事前取得すると、EngineOptions{Offline: true}でnetworkを使用せずに描画できます。cache先は既定で.makeitaquote/fonts.makeitaquote/twemojiです。

Voids API

Voidsは第三者運営の外部APIです。ローカル描画とは独立したapi/voidsパッケージに分離されています。

client, err := voids.NewClient(voids.Options{})
png, err := client.Direct(ctx, voids.Quote{Text: "hello", Username: "cat"})
hostedURL, err := client.HostedURL(ctx, voids.Quote{Text: "hello"})

コミット済みのdocs/visualを基準に全16グループ・96画像を生成し、形式、寸法、30×16 block RGB平均差を検証します。

go run ./cmd/miq-gallery --compare --out docs/visual-go
go run ./cmd/miq-gallery --offline --compare --out docs/visual-go-offline
go run ./cmd/miq-gallery --out docs/visual --site docs/index.html

既定の知覚差分閾値は0.08です。--offlineではnetwork依存27件を除く69件を検証します。--siteはJavaScriptを使わない静的gallery HTMLを生成します。

Development

go test ./...
go test -race ./...
go vet ./...
CGO_ENABLED=0 go test -tags nodynamic ./...
CGO_ENABLED=0 go build -tags nodynamic ./...

詳細はCONTRIBUTING.md、互換性テストはCOMPATIBILITY_TESTS.md、移行設計と判断記録はGO_REWRITE_DESIGN.mdを参照してください。

License and attribution

コードはMIT Licenseです。標準Unicode emoji画像にはTwemojiを使用します。公開画像にはTwemojiのCC-BY 4.0 attributionが必要です。詳細はTHIRD-PARTY-NOTICES.mdを参照してください。

Documentation

Index

Constants

View Source
const (
	MaxTextLength      = 4000
	MaxNameLength      = 128
	MaxWatermarkLength = 64
	MaxScale           = 8
)

Variables

View Source
var (
	ErrValidation = errors.New("validation error")
	ErrAsset      = errors.New("asset error")
	ErrFont       = errors.New("font error")
	ErrRender     = errors.New("render error")
	ErrAPI        = errors.New("API error")
)

Functions

func Encode

func Encode(w io.Writer, img image.Image, opts EncodeOptions) error

func EncodeBytes

func EncodeBytes(img image.Image, opts EncodeOptions) ([]byte, error)

func EncodeDataURL

func EncodeDataURL(img image.Image, opts EncodeOptions) (string, error)

func MIMEType

func MIMEType(format Format) (string, error)

Types

type AssetError

type AssetError struct {
	Source string
	Err    error
}

AssetError adds source information to an image or other asset failure.

func (*AssetError) Error

func (e *AssetError) Error() string

func (*AssetError) Unwrap

func (e *AssetError) Unwrap() error

type AvatarSizeAxis

type AvatarSizeAxis string
const (
	AvatarNativeWidth  AvatarSizeAxis = "width"
	AvatarNativeHeight AvatarSizeAxis = "height"
)

type ConversationMessage

type ConversationMessage struct {
	Text        string
	Username    string
	DisplayName string
	Avatar      ImageSource
}

type ConversationOptions

type ConversationOptions struct {
	Theme        ConversationTheme
	Width        int
	Misskey      MisskeyOptions
	OnAssetError MissingAssetBehavior
}

type ConversationTheme

type ConversationTheme string
const (
	ConversationDark  ConversationTheme = "dark"
	ConversationLight ConversationTheme = "light"
)

type EncodeOptions

type EncodeOptions struct {
	Format  Format
	Quality int
}

type Engine

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

Engine owns shared rendering, network, font, and emoji policy and is safe for concurrent render calls.

func NewEngine

func NewEngine(opts EngineOptions) (*Engine, error)

func (*Engine) RenderConversation

func (e *Engine) RenderConversation(ctx context.Context, messages []ConversationMessage, options ConversationOptions) (*image.NRGBA, error)

func (*Engine) RenderQuote

func (e *Engine) RenderQuote(ctx context.Context, quote Quote, opts RenderOptions) (*image.NRGBA, error)

func (*Engine) WriteConversation

func (e *Engine) WriteConversation(ctx context.Context, w io.Writer, messages []ConversationMessage, options ConversationOptions, encode EncodeOptions) error

func (*Engine) WriteQuote

func (e *Engine) WriteQuote(
	ctx context.Context,
	w io.Writer,
	quote Quote,
	opts RenderOptions,
	enc EncodeOptions,
) error

type EngineOptions

type EngineOptions struct {
	HTTPClient          *http.Client
	Offline             bool
	MaxAssetBytes       int64
	Fonts               []FontFace
	FontCacheDir        string
	DisableAutoFont     bool
	StrictFonts         bool
	TwemojiCacheDir     string
	AllowPrivateNetwork bool
	MaxImagePixels      int64
	ImageCacheEntries   int
	ImageCacheTTL       time.Duration
	ImageFailureTTL     time.Duration
	DisableImageCache   bool
}

EngineOptions controls shared I/O policy. More cache and font options will be added behind Engine without changing RenderQuote's signature.

type FieldError

type FieldError struct {
	Field string
	Err   error
}

FieldError reports which public input failed validation.

func (*FieldError) Error

func (e *FieldError) Error() string

func (*FieldError) Unwrap

func (e *FieldError) Unwrap() error

type FontFace

type FontFace struct {
	Family string
	Data   []byte
}

FontFace registers one regular font face under Family for the lifetime of an Engine. Data is copied by NewEngine.

type Format

type Format string
const (
	PNG  Format = "png"
	JPEG Format = "jpeg"
	JPG  Format = "jpg"
	WebP Format = "webp"
	AVIF Format = "avif"
)

func CanonicalFormat

func CanonicalFormat(format Format) (Format, error)

type ImageSource

type ImageSource interface {
	// contains filtered or unexported methods
}

ImageSource deliberately distinguishes URLs, files, bytes and already decoded images. A string is never guessed to be both a path and a URL.

func ImageBytes

func ImageBytes(b []byte) ImageSource

ImageBytes copies b so a render cannot race with a caller mutating it.

func ImageFile

func ImageFile(path string) ImageSource

func ImageURL

func ImageURL(value *url.URL) ImageSource

func ImageValue

func ImageValue(value image.Image) ImageSource

type MissingAssetBehavior

type MissingAssetBehavior string
const (
	AssetAsText MissingAssetBehavior = "text"
	AssetIgnore MissingAssetBehavior = "ignore"
	AssetThrow  MissingAssetBehavior = "throw"
)

type MisskeyOptions

type MisskeyOptions struct {
	Instances []string
	Remote    *bool
}

type Quote

type Quote struct {
	Text        string
	Avatar      ImageSource
	Username    string
	DisplayName string
	Watermark   string
}

Quote is the normalized input for one Make it a Quote image.

type RenderOptions

type RenderOptions struct {
	Theme             theme.Input
	Scale             float64
	Misskey           MisskeyOptions
	OnAssetError      MissingAssetBehavior
	BackgroundImage   ImageSource
	BackgroundFit     theme.Fit
	BackgroundOpacity *float64
	SizeToAvatar      AvatarSizeAxis
}

Directories

Path Synopsis
adapter
discord
Package discord adapts Discord messages into makeitaquote inputs.
Package discord adapts Discord messages into makeitaquote inputs.
misskey
Package misskey adapts Misskey notes into makeitaquote inputs.
Package misskey adapts Misskey notes into makeitaquote inputs.
twitter
Package twitter adapts X/Twitter post representations into makeitaquote inputs.
Package twitter adapts X/Twitter post representations into makeitaquote inputs.
api
voids
Package voids provides a client for the third-party Voids quote API.
Package voids provides a client for the third-party Voids quote API.
Package asset manages persistent fonts and Twemoji used by the renderer.
Package asset manages persistent fonts and Twemoji used by the renderer.
cmd
miq command
miq-gallery command
Command miq-gallery renders and optionally compares the Go visual gallery.
Command miq-gallery renders and optionally compares the Go visual gallery.
examples
go-basic command
go-conversation command
go-discord command
go-theme command
go-voids command
internal
gallery
Package gallery generates and compares the repository's visual gallery.
Package gallery generates and compares the repository's visual gallery.
testfixture
Package testfixture provides deterministic, non-personal image fixtures.
Package testfixture provides deterministic, non-personal image fixtures.
markup
mfm
Package mfm converts Misskey-flavoured markup to the text visible to a user.
Package mfm converts Misskey-flavoured markup to the text visible to a user.

Jump to

Keyboard shortcuts

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